동기 스레딩 vs 비동기 스레딩

동기 스레딩:
부모가 자식 스레드를 생성하고 실행하는데, 자식이 끝날 때까지 기다려야 한다.
데이터를 공유하면서 실행되고, 스레드 간 자원 공유가 가능하다.
하나의 흐름이 끝나야 다음 작업이 가능해서 효율이 낮아질 수 있고 속도가 비동기에 비해 느리다.
비동기 스레딩:
부모가 자식 스레드를 생성하지만, 기다리지 않고 바로 다음 작업을 수행한다.
자식 스레드도 독립적으로 실행되며 서로 영향을 주지 않는다. 각각 따로 실행되므로 레이스컨디션을 방지 가능하다.
멀티스레드 환경에서 동시 처리가 가능해 성능이 향상된다.
의문1: 동기 스레딩은 병행성과, 비동기 스레딩은 병렬성과 매칭되나?
이벤트 기반 비동기 방식은 병행성도 제공한다!
1. 동기 방식
public class SyncThreadExample {
public static void main(String[] args) {
System.out.println("1. 파일 읽기 시작");
readFile(); // 파일 읽는 동안 CPU가 대기 (Blocking)
System.out.println("2. 파일 읽기 완료");
System.out.println("3. 다음 작업 실행");
}
public static void readFile() {
try {
Thread.sleep(3000); // 파일 읽는 동안 블로킹
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
한 번에 하나의 스레드만 실행한다. 스레드가 I/O 작업을 요청하면 blocking상태로 기다리고, 작업이 끝나야 다음 작업을 실행할 수 있다.

thread.sleep()을 호출한 스레드가 cpu를 사용하지 않고 대기상태로 빠진다.
2. 비동기 방식
public class AsyncThreadExample {
public static void main(String[] args) {
System.out.println("1. 파일 읽기 시작");
CompletableFuture.runAsync(() -> readFile()) // 비동기 실행
.thenRun(() -> System.out.println("2. 파일 읽기 완료"));
System.out.println("3. 다음 작업 실행 (파일 읽는 동안 대기 없음)");
try {
Thread.sleep(4000); // 프로그램 종료 방지용
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void readFile() {
try {
Thread.sleep(3000); // 파일 읽는 동안 CPU는 다른 작업 실행 가능
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
I/O작업을 요청한 후 결과를 기다리지 않고 다음 작업을 실행한다.
요청이 끝나면 os가 인터럽트나 이벤트를 통해 응답을 전달하고, cpu는 블로킹 없이 다른 작업을 실행할 수 있다.

결론:
비동기는 병행성과 병렬성 모두 가능하다. 싱글코어 환경에서는 병행성을, 멀티코어 환경에서는 병렬성을 제공한다.
운영체제에서의 비동기 병행성은 싱글 스레드에서 여러 작업을 동시 실행하는 것처럼 보이는 방식이다.
비동기 I/O(async I/O, non-blocking i/o) -> os가 작업을 비동기적으로 처리하고 응답한다.
이벤트 기반 프로그램이 -> os가 여러 요청을 대기하면서 실행한다.
운영체제 관점에서 비동기 병행성은 cpu 스케줄링과 컨텍스트 스위칭을 활용하여 구현되고, 멀티태스킹을 가능하게 하는 주요 원리다.
의문2: 동기 방식에서의 컨텍스트 스위칭은 비동기 방식과 어떤 차이가 있는가?
컨텍스트 스위칭은 비동기/동기 방식 모두에서 발생하지만, 빈도와 방식이 다르다.
동기 방식은 "작업이 끝난 후" 실행하고, I/O 요청이 발생하면 blocking 상태가 되고 해당 작업이 끝날 때 까지 cpu가 스레드를 실행시키지 않는다.
비동기 방식은 "작업 실행 중"에도 가능하다. I/O 요청이 발생하면 컨텍스트 스위칭을 통해 해당 작업을 기다리지 않고 다른 작업을 실행할 수 있다.
따라서, 비동기 방식에서의 컨텍스트 스위칭 횟수가 훨씬 더 많다!
의문3: 동기 실행은 어차피 요청이 끝난 후 실행되므로, Race condition이 발생할 일이 없는 거 아닌가?
Race condition : 여러 개의 스레드 또는 프로세스가 동시에 같은 자원에 접근하여, 실행 순서에 따라 결과가 달라지는 상황이다. 누가 먼저 실행되느냐에 따라 결과가 달라지기 때문에 예측불가능한 버그가 발생한다.
public class RaceConditionExample {
private static int counter = 0;
public static void main(String[] args) {
Thread task1 = new Thread(() -> {
for (int i = 0; i < 10000; i++) {
counter++; // 요청 1
}
});
Thread task2 = new Thread(() -> {
for (int i = 0; i < 10000; i++) {
counter++; // 요청 2
}
});
task1.start();
task2.start();
try {
task1.join();
task2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("최종 카운트 값: " + counter);
}
}
해당 코드처럼 동기 실행이지만 공유 자원이 있는 경우에 레이스컨디션이 발생 가능하다.


(최종값이 항상 20000이 아닐 수도 있고, 동시성 이슈의 원인 중 하나가 바로 이 레이스컨디션이다.)
(+ deadlock, starvation 등이 또 있다)
따라서 "동기화" 를 통해 동기 실행 방식에서의 동시성 이슈를 처리한다.(synchronized, atomic, concurrentHashMap, threadlocal)
db에서의 동시성 이슈를 처리하기 위한 플랜은 lock, db transaction이 존재한다.
그럼 비동기 방식에서는 왜 레이스컨디션이 방지될까? 비동기 방식에서는 각 작업이 독립적으로 실행되어서, 각 작업이 다른 리소스를 사용하게 설계하기에 레이스 컨디션 자체가 발생하지 않는다!
애초에 공유 자원을 최소화하기 떄문에, 원천적으로 방지할 수 있다!
(하지만 공유 자원이 존재한다면? -> 동시성 이슈는 발생 가능하다.)
Thread Pool
웹 서버는 사용자의 요청을 받을 때 마다 새로운 스레드를 생성해서 처리한다.
하지만 매번 스레드를 새로 만들면 시간이 오래 걸리고 자원을 많이 소비한다. 특히 요청이 많아지면 CPU와 메모리를 너무 많이 사용해서 서버가 느려질 수도 있다.
이 문제를 해결하는 것이 Thread Pool이고, 프로그램이 실행될 때 미리 일정 개수의 스레드를 만들어 두는 것이다.
요청이 들어오면 새로운 스레드를 만들지 않고 미리 만들어둔 스레드를 사용한다.
스레드가 작업을 마치면 다시 풀에 반환돼서 다음 요청을 기다리고, 만약 사용 가능한 스레드가 없으면 새로운 스레드가 생성되거나 요청 대기 상태로 기다린다. 이는 비동기 작업을 수행할때도 효과적으로 동작한다.
새로운 스레드가 생성되는 기준은, 사용자의 설정에 따라 다르다. java에서는 threadPoolExecuter 설정을 통해 스레드 설정이 가능하다.
의문: 왜 비동기 작업에서 "thread pool"이 효과적일까?
일단, 비동기 작업은 요청을 기다리지 않고 바로 다음 작업을 실행할 수 있도록 한다. 그런데 비동기 작업을 수행하려면 결국 스레드가 필요하고, 매번 스레드를 필요할 때 마다 생성하면 문제가 발생한다.
어떤 문제들이 발생할까?
1. 오버헤드 증가: 스레드를 만들고-제거하는 데 비용이 크다. os측면에서 스레드 관리 비용이 증가한다
2. 메모리 사용량 증가: 너무 많은 스레드가 동시에 실행되면 메모리 부족 가능성이 증가한다
3. 컨텍스트 스위칭 비용 증가
그렇다면 스레드 풀이 어떻게 해결해줄까?
1. 오버헤드 감소: 미리 만들어둔 스레드를 재사용하므로 성능이 향상된다
2. 메모리 사용량 최적화: 제한된 개수의 스레드만 실행되므로, 메모리 낭비가 방지된다
3. 컨텍스트 스위칭 비용 감소
'독서기록장' 카테고리의 다른 글
5. Real MySQL 8장 인덱스를 잘 걸자 (0) | 2025.03.20 |
---|---|
3. 운영체제 3장 프로세스 간 통신 (0) | 2025.01.30 |
2. 운영체제 3장 프로세스 기초, 컨텍스트 스위칭 (1) | 2025.01.29 |