1. 기본 개념
개념 | 정의 | 예시 |
동기(Synchronous) | 작업 요청 후 결과를 받을 때까지 기다림 | 전화 통화 |
비동기(Asynchronous) | 요청 후 결과를 기다리지 않고, 완료되면 알림(콜백, Future,...) | 문자 메세지 |
블로킹(Blocking) | 요청한 작업이 끝날 때까지 현재 실행중인 작업이 멈춤 | 엘레베이터에서 기다리는 것 |
논블로킹(Non-blocking) | 요청한 작업이 끝나지 않아도 다른 작업을 계속 수행 가능 | 식당에서 주문 후 다른 일을 하는 것 |
2. 동기(Synchronous) vs 비동기(Asynchronous)
동기(Synchronous)
- 작업을 요청한 후 응답을 받을 때까지 기다려야함
- 요청한 작업이 끝나야 다음 작업을 수행 가능
1) 예제(동기 방식) - Java
public class SyncExample {
public static void main(String[] args) {
System.out.println("요청 시작");
String result = blockingTask(2);//결과를 기다림
System.out.println("result = " + result);
System.out.println("작업 완료");
}
private static String blockingTask(int n) {
try {
for (int i = 1; i < n + 1; i++) {
System.out.printf("----- %d초 대기 -----\n", i);
Thread.sleep(i * 1000L);
}
} catch (Exception ignored){}
return "동기 작업 완료";
}
}
2) 출력결과
- blockingTask(n) 메서드가 끝나야 다음 코드가 실행됨
요청 시작
----- 1초 대기 -----
----- 2초 대기 -----
result = 동기 작업 완료
작업 완료
비동기(Asynchronous)
- 요청 후 결과를 기다리지 않고 즉시 다음 작업을 수행할 수 있음
- 작업이 끝나면 콜백(callback) 또는 Future/CompletableFuture로 결과를 받아 처리
1) 예제(비동기 방식 - Java/CompletableFuture
public class AsyncExample {
public static void main(String[] args) {
System.out.println("작업 시작");
CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {
try {
for (int i = 1; i < 2 + 1; i++) {
System.out.printf("----- %d초 대기 -----\n", i);
Thread.sleep(i * 1000L);
}
} catch (Exception e) {}
return "\n비동기 작업 완료!";
}).thenAccept(System.out::println);
System.out.println("메인 쓰레드 종료");
future.join();//비동기 작업이 끝날 때까지 대기
}
}
2) 출력결과
- thenAccept() : 콜백에 실행될 때까지 메인 스레드는 멈추지 않고 다른 작업을 수행
- CompletableFuture.join : 비동기 작업이 완료될 때까지 블로킹방식으로 대기후 결과반환
작업 시작
----- 1초 대기 -----
메인 쓰레드 종료
----- 2초 대기 -----
비동기 작업 완료!
3. 블로킹(Blocking) vs 논블로킹(Non-blocking)
블로킹(Blocking)
- 현재 실행 중인 스레드가 요청한 작업이 끝날 때까지 대기함
- CPU 리소스를 비효율적으로 사용하게 될 수 있음
1) 예제 (블로킹 방식 - Java)
public class BlockingExample {
public static void main(String[] args) {
String filePath = "src/main/resources/data.json";
ObjectMapper objectMapper = new ObjectMapper();
try {
// 파일을 읽는 동안 다른 작업 불가능(블로킹)
File file = new File(filePath);
Map<String, Object> dataMap = objectMapper.readValue(file, new TypeReference<>() {});
System.out.println("블로킹 방식으로 읽은 JSON 데이터");
String prettyJson = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(dataMap);
System.out.println(prettyJson);
} catch (Exception ignore){}
}
}
2) 출력결과
- 파일을 읽기 작업이 끝날때 까지 다른 처리는 실힝되지 않음
블로킹 방식으로 읽은 JSON 데이터
{
"name" : "SeongSik Oh",
"age" : 32,
"job" : "Backend Dev",
"isEmployed" : true
}
논블로킹(Non-blocking)
- 작업 요청 후 즉시 반환되며, 작업이 끝나지 않아도 다른 작업을 수행 가능
- 일반적으로 이벤트 기반(eveng-driven) 방식으로 처리됨
1) 예제 (논블로킹 방식 - Java)
public class NonBlockingExample {
public static void main(String[] args) {
String filePath = "src/main/resources/data.json";
ObjectMapper objectMapper = new ObjectMapper();
// 비동기 작업으로 파일 읽기
CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {
try {
// 파일을 비동기적으로 읽기(논블로킹)
File file = new File(filePath);
Map<String, Object> dataMap = objectMapper.readValue(file, new TypeReference<>() {});
return dataMap;
} catch (Exception ignore) {
return null;
}
}).thenAccept(dataMap -> {
if(dataMap != null){
try {
System.out.println("\n논블로킹 방식으로 읽은 JSON 데이터");
String prettyJson = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(dataMap);
System.out.println(prettyJson);
} catch (Exception ignore) {}
}
});
// 파일 읽기 작업이 완료되기 전에 다른 작업을 할 수 있음
System.out.println("메인 메서드 실행종료");
future.join();
}
}
2) 출력결과
- 파일을 읽는 동안 다른 작업을 계속 수행할 수 있음
메인 메서드 실행종료
논블로킹 방식으로 읽은 JSON 데이터
{
"name" : "SeongSik Oh",
"age" : 32,
"job" : "Backend Dev",
"isEmployed" : true
}
4. 동기/비동기 vs 블로킹/논블로킹 관계
동기/비동기 | 블로킹/논블로킹 | 설명 |
동기 + 블로킹 | 요청한 작업이 끝날 때까지 대기 | 일반적인 함수호출 |
동기 + 논블로킹 | 요청 후 바로 반환처리되지만, 결과를 즉시 확인행함 | Future.get() 사용 |
비동기 + 블로킹 | 비동기 작업을 실행하지만, get()호출 시 블로킹된 | CompletableFuture.get() |
비동기 + 논블로킹 | 요청 후 바로 반환되고, 결과는 콜백 또는 이벤크로 처리됨 | CompletableFuture.thenAccept(), WebFlux |
- (Q) 비동기, 논블로킹의 차이는?
- 비동기는 결과를 기다리지 않고 작업을 요청한 후 다른 작업을 진행하는 방식입니다. 하지만 일부 비동기 작업은 결과를 받을 때까지 스레드가 블로킹될 수 있다
- 논블로킹은 스레드가 작업을 기다리지 않고 다른 작업을 할 수 있는 방식이다. I/O 작업에서 논블로킹 방식이 활용되며, 처리 중 다른 작업을 병렬적으로 수행할 수 있다.따라서, 비동기는 작업의 순서와 결과 처리 방식에 관한 개념이고, 논블로킹은 스레드의 차단 여부와 관련된 개념이다.
- 동기(Synchronous): 요청한 작업이 끝날 때까지 기다림.
- 비동기(Asynchronous): 요청 후 즉시 반환, 완료되면 콜백으로 처리.
- 블로킹(Blocking): 작업이 끝날 때까지 현재 스레드가 멈춤.
- 논블로킹(Non-blocking): 작업이 진행되는 동안에도 다른 작업을 수행할 수 있음.
5. WebFlux
- WebFlux는 Reactor 기반으로 동작하는 비동기 논블로킹 프레임워크이다. 기본적으로 단일 쓰레드 모델로 동작할 수 있지만, 모든 작업을 하나의 쓰레드에서 처리하는 것은 아니다. 대신, 논블로킹 방식으로 다수의 작업을 효율적으로 처리하며, 필요에 따라 스레드 풀을 사용해 비동기 작업을 처리한다.
WebFlux와 스레드
- 비동기 논블로킹 처리를 위해 Reactor의 스케줄러(Scheduler)를 활용하여 여러 스레드를 사용할 수 있다.
WebFlux 동작방식
- 단일 스레드로 요청 처리가능
- 기본적으로 WebFlux는 단일 이벤트 루프 방식으로 작동할 수 있다. 논블로킹 방식이기 때문에, 한 요청이 대기중일 때 다른 요청을 처리할 수 있다.
- I/O 작업은 비동기적으로 처리
- HTTP 요청을 보내거나, DB에서 데이터를 조회하거나, 다른 외부 서비스를 호출하는 등의 I/O 작업은 비동기 논블로킹 방식으로 처리된다. WebClient 같은 비동기 API를 사용하면, 해당 작업을 다른 스레드에서 실행하지 않고, 현재 스레드를 차단하지 않고 처리한다.(I/O 작업은 결과가 도달할 때까지 기다리지 않고 다른 작업을 계속 처리할 수 있다.)
- 스레드 풀 사용
- WebFlux는 기본적으로 I/O 작업을 처리하기 위해 스레드 풀을 사용한다. 예를 들어, HTTP 요청이나 DB 연결 등에서 비동기 작업이 이루어질 때, Webflux는 "boundedElastic()/parallel()"와 같은 스케줄러를 사용하여 추가적인 스레드를 활용할 수 있다.
- 이를 통해 논블로킹 I/O를 최적화하고, CPU 집약적인 작업을 병렬로 처리할 수 있다.
WebClient와 스레드
- WebClient는 비동기 논블로킹 방식으로 HTTP 요청을 처리하는 클라이언트이다. WebClient의 호출, 응답 처리는 기본적으로 Reactor 스케줄러에서 실행된다.
기본적인 WebClient 흐름
- retreive() : 비동기적으로 HTTP 요청을 보낸다. 이때 I/O 작업은 논블로킹 방식으로 처리되며, 별도의 스레드를 사용하여 요청을 보낸다.
- doOnSuccess(), doOnError() : 응답이 성공/에러가 발생하면, 해당 작업이 Reactor 스케줄러에서 실행된다. 기본적으로 WebClient는 Reactor의 기본 스케줄러를 사용하므로, 이 메서드들은 I/O 작접을 처리한 스레드에서 실행될 수 있다.
- subscribe() : 실제로 비동기 작업을 구독하는 메서드이다. subscribe()를 호출할 스레드에서 이 작업이 구독된다.
'백엔드 면접준비 > Java' 카테고리의 다른 글
JAVA 기본 (1) | 2025.03.16 |
---|---|
8.JAR (1) | 2025.03.10 |
6. Garbage Collection(GC) (1) | 2025.03.07 |
4. Java Annotation (0) | 2025.03.07 |
3. Java Collection Framework(JCF) (1) | 2025.02.03 |