웹플럭스를 공부하면서 [WebFlux] 카테고리에 학습내용을 정리 할 예정입니다.
그 중에 첫 번째로 동기와 비동기, 그리고 blocking 과 non-blocking 에 대해서 정리해보고자 합니다.
🪴 동기와 비동기
먼저, 동기와 비동기에 대해서 정리해보겠습니다.

호출하는 함수와 호출 당하는 함수에 대해서 많이 언급을 할 예정입니다.
이것을 각각 Caller(호출하는 함수) 와 Callee(호출 당하는 함수) 로 부르겠습니다.
아래 두 모델을 확인해보겠습니다.
< A 모델 >
@Slf4j
public class A {
public static void main(String[] args) {
log.info("Start main");
var result = getResult();
var nextValue = result + 1;
assert nextValue == 1;
log.info("Finish main");
}
public static int getResult() {
log.info("Start getResult");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
var result = 0;
try {
return result;
} finally {
log.info("Finish getResult");
}
}
}
- A 모델은 main 이 getResult 결과에 관심이 있습니다.
- 그래서 main 은 getResult 결과를 이용해서 다음 코드를 실행합니다.
< B 모델 >
@Slf4j
public class B {
public static void main(String[] args) {
log.info("Start main");
getResult(new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
var nextValue = integer + 1;
assert nextValue == 1;
}
});
log.info("Finish main");
}
public static void getResult(Consumer<Integer> cb) {
log.info("Start getResult");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
var result = 0;
cb.accept(result);
log.info("Finish getResult");
}
}
- B 모델은 main 이 getResult 의 결과에 관심이 없습니다.
- 그래서 getResult 는 결과를 이용해서 함수형 인터페이스를 실행합니다.
이것을 정리해서 다시 말하면 아래와 같습니다.
A 모델 (동기) | B 모델 (비동기) |
![]() |
![]() |
동기에서는 Caller 는 Callee 의 결과에 관심이 있습니다.
caller는 결과를 이용해서 action 을 수행합니다.
비동기에서는 Caller 는 Callee 의 결과에 관심이 없습니다.
Callee 는 결과를 이용해서 callback 을 수행합니다.
🪴 Blocking 과 Non-blocking
다음으로 Blocking 과 Non-blocking 에 대해서 정리해보겠습니다.
A 모델 (blocking) | B 모델 (non-blocking) |
![]() |
![]() |
A모델 (blocking) 에서는 callee 를 호출한 후, callee 가 완료되기 전까지 caller 가 아무것도 할 수 없습니다.
B모델 (non-blocking) 에서는 callee 를 호출한 후, callee 가 완료되지 않더라도 caller 는 본인의 일을 할 수 있습니다.
정리해보겠습니다.
Blocking
- caller 는 callee 가 완료될 때까지 대기한다.
- 제어권을 callee 가 가지고 있다.
- caller 와 다른 별도의 thread 가 필요하지 않다. (혹은 thread 를 추가로 쓸 수도 있음)
- caller 와 callee 가 동일한 경우가 많다. blocking 이기 때문에 하나의 thread 에서 caller, callee 가 일어나는 경우가 많기 때문이다.
Non-blocking
- caller 는 callee 를 기다리지 않고 본인의 일을 한다.
- 제어권을 caller 가 가지고 있다.
- caller 와 다른 별도의 thread 가 필요하다.
🪴 동기랑 블로킹이 헷갈려요 (+비동기, 논블로킹)
동기/비동기는 결과를 어떻게 받고 처리할 것인지에 관한 것이고,
블로킹/논블로킹은 함수 호출 시 제어권을 넘기는지 유지하는지에 관한 것 입니다.
동기 vs 블로킹
동기는 작업 결과의 처리방식에 관한 개념입니다.
호출자가 호출받는 함수의 결과를 직접 받아서 처리합니다.
따라서 작업의 순서가 보장되고, 흐름이 예측 가능합니다.
블로킹은 제어권에 관한 개념입니다.
호출자가 함수를 호출하면 제어권을 넘겨주고, 호출된 함수의 작업이 끝날 때까지 다음 코드로 진행하지 못하고 기다립니다.
비동기 vs 논블로킹
비동기는 작업 결과의 처리방식에 관한 개념입니다.
호출자가 호출받는 함수의 결과를 직접 처리하지 않고 콜백, Promiss, Future 등을 통해 나중에 처리합니다.
논블로킹은 제어권에 관한 개념입니다.
호출자가 함수를 호출해도 제어권을 유지하며, 호출된 함수의 완료를 기다리지 않고 다음 코드를 계속 실행할 수 있습니다.
🪴 동기 Blocking, 비동기 Blocking, 동기 Non-blocking, 비동기 Non-blocking
동기 Blocking | 비동기 Blocking |
![]() |
![]() |
동기 Non-blocking | 비동기 Non-blocking |
![]() |
![]() |
- 동기 Blocking : Caller 는 아무것도 할 수 없는 상태가 된다. 결과를 얻은 후 직접 처리한다.
- 비동기 Blocking : Caller 는 아무것도 할 수 없는 상태가 된다. 결과는 Callee 가 처리한다.
- 동기 Non-blocking : Caller 는 자기 할 일을 할 수 있다. 결과를 얻은 후 직접 처리한다.
- 비동기 Non-blocking : Caller 는 자기 할 일을 할 수 있다. 결과는 Callee 가 처리한다.
이 개념들을 조금 더 이해하기 위해 간단한 코드로 예시를 들어보겠습니다.
동기 Blocking | 동기 Non-blocking |
- 파일을 읽고 결과를 직접 처리 (동기) - 읽기가 끝날 때까지 대기 (블로킹) ![]() |
- 준비됐는지 직접 계속 확인 (동기) - 다른 작업 수행 가능 (논블로킹) ![]() |
비동기 Blocking | 비동기 Non-blocking |
- 콜백으로 결과 처리 (비동기) - 스레드는 join 으로 대기 (블로킹) ![]() |
- 콜백으로 결과 처리 (비동기) - 다른 작업 계속 수행 (논블로킹) ![]() |
앞으로 다룰 WebFlux 는 비동기 Non-blocking 방식입니다.
그래서 Non-blocking 에 대해서 조금 더 깊게 정리를 해볼 예정입니다.