Skip to main content Link Menu Expand (external link) Document Search Copy Copied

๋‹ค๋“ค ๋™๊ธฐ/๋น„๋™๊ธฐ์™€ Blocking/Non-blocking ์„ ํ•œ๋ฒˆ์”ฉ ๋“ค์–ด๋ณด์…จ์„๊ฑฐ์—์š”.

ํ•˜์ง€๋งŒ Spring-Java ์—์„œ ์ด๋ฅผ ์„ž์€ ์กฐํ•ฉ์— ๋Œ€ํ•œ ์‹ค์ œ ๊ตฌํ˜„ ๋ฐฉ์‹์„ ์„ค๋ช…ํ•˜๋Š” ๊ธ€์ด ๋ณ„๋กœ ์—†๋”๋ผ๊ตฌ์š”.

๊ทธ๋ž˜์„œ! ์˜ค๋Š˜์˜ ํฌ์ŠคํŒ…์€ ํ•ด๋‹น ๊ตฌํ˜„ ๋ฐฉ์‹์— ๋Œ€ํ•ด ์ž์„ธํžˆ ์„ค๋ช…ํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

1. ๋น„๋™๊ธฐ/๋™๊ธฐ์™€ Blocking/Non-blocking ์˜ ์ฐจ์ด์ 

1.1 Sync vs Async

ํ˜ธ์ถœ๋˜๋Š” ํ•จ์ˆ˜์˜ ์ž‘์—… ์™„๋ฃŒ ์—ฌ๋ถ€๋ฅผ ๋ˆ„๊ฐ€ ์‹ ๊ฒฝ์“ฐ๋ƒ๊ฐ€ ๊ด€์‹ฌ์‚ฌ!

A ์Šค๋ ˆ๋“œ๊ฐ€ B ์Šค๋ ˆ๋“œ์—๊ฒŒ ์š”์ฒญํ–ˆ์„ ๋•Œ,

  • Sync : A ์Šค๋ ˆ๋“œ๋Š” ์ง์ ‘์ ์œผ๋กœ B ์Šค๋ ˆ๋“œ์—๊ฒŒ ๊ฒฐ๊ณผ์š”์ฒญ ๋ฉ”์„ธ์ง€๋ฅผ ์ „์†กํ•จ. ์ฆ‰, ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๊ฐ€ ์ž‘์—… ์™„๋ฃŒ ์—ฌ๋ถ€๋ฅผ ์ฒดํฌํ•จ.
  • Async : B ์Šค๋ ˆ๋“œ๊ฐ€ A ์Šค๋ ˆ๋“œ์—๊ฒŒ ๊ฒฐ๊ณผ๋ฅผ ์ „๋‹ฌํ•จ์œผ๋กœ์จ, ๊ฒฐ๊ณผ๋ฅผ ๊ฐ„์ ‘์ ์œผ๋กœ ๋ฐ›์Œ. ์ฆ‰, ์„œ๋ธŒ ์Šค๋ ˆ๋“œ๊ฐ€ ์ž‘์—… ์™„๋ฃŒ ์—ฌ๋ถ€๋ฅผ ๋ฉ”์ธ์— ์ „์†กํ•จ.

1.2 Blocking vs Non-blocking

ํ˜ธ์ถœ๋˜๋Š” ํ•จ์ˆ˜๊ฐ€ ๋ฐ”๋กœ ๋ฆฌํ„ดํ•˜๋Š๋ƒ ๋งˆ๋Š๋ƒ๊ฐ€ ๊ด€์‹ฌ์‚ฌ!

A ์Šค๋ ˆ๋“œ๊ฐ€ B ์Šค๋ ˆ๋“œ์—๊ฒŒ ์š”์ฒญํ–ˆ์„ ๋•Œ,

  • Blocking : A ์Šค๋ ˆ๋“œ๋Š” ๋ฆฌํ„ด์„ ๋ฐ›๊ธฐ ์ „๊นŒ์ง€ ๋‹ค์Œ์˜ ๋ช…๋ น์„ ์‹คํ–‰ํ•  ์ˆ˜ ์—†์Œ
  • Non-blocking : ๊ทธ๋ ‡์ง€ ์•Š๊ณ  ์š”์ฒญ๊ณผ ๋™์‹œ์— ๋‹ค์Œ์˜ ๋ช…๋ น์„ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Œ

2. ์กฐํ•ฉ์— ๋”ฐ๋ฅธ 4 ๊ฒฝ์šฐ์˜ ๊ตฌํ˜„์ฐจ์ด์  (feat. CompletableFuture + WebFlux)

๊ธฐ๋ณธ์ ์œผ๋กœ CompletableFuture ๋ฅผ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋ฉด, ๊ฒฐ๊ณผ๋Š” .get()์ด๋‚˜ .join()๋ฉ”์†Œ๋“œ๋ฅผ ํ†ตํ•ด ๋ฉ”์ธ์Šค๋ ˆ๋“œ์—์„œ ์ˆ˜์‹ ํ•ด์•ผํ•ด์š”. ๊ทธ๋ฆฌ๊ณ  ์ด ๋ฉ”์†Œ๋“œ๋“ค์€ Blocking ๋ฉ”์†Œ๋“œ์ž…๋‹ˆ๋‹ค. ์ฆ‰, CompletableFuture ๋ฅผ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋ฉด, ์‹ค์งˆ์ ์ธ Non-Blocking ๊ตฌํ˜„์ด ํž˜๋“ค์–ด์š”. ์™œ๋ƒํ•˜๋ฉด ์–ด์จ‹๋“  ๊ฒฐ๊ณผ๋ฅผ Blocking ์œผ๋กœ ๋ฐ›๊ธฐ ๋•Œ๋ฌธ์ด์ฃ !.

๊ทธ๋ž˜์„œ ์ €๋Š” Flux ์™€ Sink ๊ฐ์ฒด๋ฅผ ํ†ตํ•ด Non-blocking ๊ตฌํ˜„์„ ํ•˜์˜€์–ด์š”. WebFlux ํฌ์ŠคํŒ…์—์„œ ๋‹ค์–‘ํ•œ ์˜ˆ์‹œ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

img Reference : https://www.inflearn.com/news/72620

2.1 Async + Blocking( CompletableFuture )

  • CompletableFuture ๋ฅผ ํ™œ์šฉํ•œ ์ฝ”๋“œ
void async_blocking() throws ExecutionException, InterruptedException {
    ThreadPoolTaskExecutor t = getThreadPoolTaskExecutor();
    System.out.println("[Thread 1] - ์ž‘์—… ์‹œ์ž‘ํ• ๊ฒŒ์š”");

    // Async
    CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
        try {Thread.sleep(500);} catch (InterruptedException e) {throw new RuntimeException(e);}
        System.out.println("[Thread 2] - [Thread 1]์œผ๋กœ๋ถ€ํ„ฐ ์ „๋‹ฌ๋ฐ›์€ ์ž‘์—… ์ฒ˜๋ฆฌํ• ๊ฒŒ์š”");
        return "Thread 2์˜ ๊ฒฐ๊ณผ๋ฌผ";
    },t);

    // Blocking
    String result = completableFuture.get();

    System.out.println("[Thread 1] - ๋๋‚ฌ๊ตฐ์š”! ๊ฒฐ๊ณผ๋ฌผ์€ : \""+result+"\", ์ด์ œ ๋‹ค์Œ ์ž‘์—… ์ˆ˜ํ–‰ํ• ๊ฒŒ์š”");
    System.out.println("[Thread 1] ๋‹ค์Œ ์ž‘์—… ์ˆ˜ํ–‰ ์ค‘...");

    t.destroy();
}
  • ๊ฒฐ๊ณผ
[Thread 1] - ์ž‘์—… ์‹œ์ž‘ํ• ๊ฒŒ์š”
[Thread 2] - [Thread 1]์œผ๋กœ๋ถ€ํ„ฐ ์ „๋‹ฌ๋ฐ›์€ ์ž‘์—… ์ฒ˜๋ฆฌํ• ๊ฒŒ์š”
[Thread 1] - ๋๋‚ฌ๊ตฐ์š”! ๊ฒฐ๊ณผ๋ฌผ์€ : "Thread 2์˜ ๊ฒฐ๊ณผ๋ฌผ", ์ด์ œ ๋‹ค์Œ ์ž‘์—… ์ˆ˜ํ–‰ํ• ๊ฒŒ์š”
[Thread 1] ๋‹ค์Œ ์ž‘์—… ์ˆ˜ํ–‰ ์ค‘...

2.2 Async + Non-blocking( CompletableFuture + WebFlux )

  • CompletableFuture ์— WebFlux ๋ฅผ ์ถ”๊ฐ€ํ•œ ์ฝ”๋“œ
void async_non_blocking() throws ExecutionException, InterruptedException {
    ThreadPoolTaskExecutor t = getThreadPoolTaskExecutor();
    Sinks.Many<Object> sinks = Sinks.many().replay().all();

    System.out.println("[Thread 1] - ์ž‘์—… ์‹œ์ž‘ํ• ๊ฒŒ์š”");

    // Async
    CompletableFuture.runAsync(() -> {
        try {Thread.sleep(500);} catch (InterruptedException e) {throw new RuntimeException(e);}
        System.out.println("[Thread 2] - [Thread 1]์œผ๋กœ๋ถ€ํ„ฐ ์ „๋‹ฌ๋ฐ›์€ ์ž‘์—… ์ฒ˜๋ฆฌํ• ๊ฒŒ์š”");
        sinks.tryEmitNext("Thread 2์˜ ๊ฒฐ๊ณผ๋ฌผ");
    },t);

    // Non-Blocking
    sinks.asFlux().log().subscribe(result->{
        System.out.println("[Thread 1] - ๋๋‚ฌ๊ตฐ์š”! ๊ฒฐ๊ณผ๋ฌผ์€ : \""+result+"\", ์ด์ œ ๋‹ค์Œ ์ž‘์—… ์ˆ˜ํ–‰ํ• ๊ฒŒ์š”");
    });

    System.out.println("[Thread 1] ๋‹ค์Œ ์ž‘์—… ์ˆ˜ํ–‰ ์ค‘...");

    try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}
    t.destroy();
}
  • ๊ฒฐ๊ณผ
[Thread 1] - ์ž‘์—… ์‹œ์ž‘ํ• ๊ฒŒ์š”
[Thread 1] INFO reactor.Flux.SinkManyReplayProcessor.1 -- | onSubscribe([Fuseable] SinkManyReplayProcessor.ReplayInner)
[Thread 1] INFO reactor.Flux.SinkManyReplayProcessor.1 -- | request(unbounded)
[Thread 1] ๋‹ค์Œ ์ž‘์—… ์ˆ˜ํ–‰ ์ค‘...
[Thread 2] - [Thread 1]์œผ๋กœ๋ถ€ํ„ฐ ์ „๋‹ฌ๋ฐ›์€ ์ž‘์—… ์ฒ˜๋ฆฌํ• ๊ฒŒ์š”
[Thread 2] INFO reactor.Flux.SinkManyReplayProcessor.1 -- | onNext(Thread 2์˜ ๊ฒฐ๊ณผ๋ฌผ)
[Thread 1] - ๋๋‚ฌ๊ตฐ์š”! ๊ฒฐ๊ณผ๋ฌผ์€ : "Thread 2์˜ ๊ฒฐ๊ณผ๋ฌผ", ์ด์ œ ๋‹ค์Œ ์ž‘์—… ์ˆ˜ํ–‰ํ• ๊ฒŒ์š”

2.3 Sync + Blocking( IORead )

์ด ๋ถ€๋ถ„์€ file.read(), file.write() ์™€ ๊ฐ™์ด ๊ฐ„๋‹จํ•œ ์˜ˆ์‹œ์ด๊ธฐ๋•Œ๋ฌธ์— ์ƒ๋žตํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

2.4 Sync + Non-blocking( CompletableFuture )

์ด ๋ถ€๋ถ„์€ ๊ฐ„๋žตํ•œ ์˜ˆ์‹œ๋ฅผ ๋“ค๊ธฐ ์œ„ํ•ด CompletableFuture ์„ ์‚ฌ์šฉํ–ˆ์–ด์š”. ํ•˜์ง€๋งŒ! ์ด ์˜ˆ์‹œ๋Š” ์™„๋ฒฝํ•œ Sync + Non-blocking ์˜ˆ์‹œ๊ฐ€ ์•„๋‹ˆ๋ผ๋Š” ์ ์„ ์•Œ์•„์ฃผ์„ธ์š”. ์™œ๋ƒํ•˜๋ฉด ์•„๋ž˜ ์ฝ”๋“œ์˜ completableFuture.get() ๋Š” ๊ฒฐ๊ตญ Blocking ์ด๊ฑฐ๋“ ์š”. ๋‹ค๋งŒ ์ด์ „์— completableFuture.isDone() ์œผ๋กœ Thread 1 ์ด ์ง์ ‘์ ์œผ๋กœ Thread 2 ์—๊ฒŒ ์ž‘์—…์™„๋ฃŒ์—ฌ๋ถ€๋ฅผ ๋ฌผ์–ด๋ณด๊ธฐ๋•Œ๋ฌธ์— ์•„๋ž˜์™€ ๊ฐ™์€ ์˜ˆ์‹œ๋ฅผ ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. ์‹ค์ œ Sync + Non-blocking ์€ asyncFileChannel.read ์™€ ๊ฐ™์€ ์˜ˆ์‹œ์ž…๋‹ˆ๋‹ค.

void sync_non_blocking() throws ExecutionException, InterruptedException {
    ThreadPoolTaskExecutor t = getThreadPoolTaskExecutor();
    System.out.println("[Thread 1] - ์ž‘์—… ์‹œ์ž‘ํ• ๊ฒŒ์š”");
    
    // Async ์ด์ง€๋งŒ, ์•„๋ž˜์˜ completableFuture.isDone()์„ ํ†ตํ•œ callBack ๋ฌด์‹œ๋กœ ๊ฒฐ๋ก ์€ Sync
    CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
        System.out.println("[Thread 2] - [Thread 1]์œผ๋กœ๋ถ€ํ„ฐ ์ „๋‹ฌ๋ฐ›์€ ์ž‘์—… ์ฒ˜๋ฆฌํ• ๊ฒŒ์š”");
        try {Thread.sleep(500);} catch (InterruptedException e) {throw new RuntimeException(e);}
        return "Thread 2์˜ ๊ฒฐ๊ณผ๋ฌผ";
    },t);

    // Non-Blocking
    while(!completableFuture.isDone()){
        Thread.sleep(200);
        System.out.println("[Thread 1] - Thread 2๋‹˜ ์ž‘์—…์ด ๋๋‚ฌ๋‚˜์š”? ๊ทธ๋™์•ˆ ์ €๋Š” ๋‹ค๋ฅธ์ผ ์ข€ ํ• ๊ฒŒ์š”");
        System.out.println("[Thread 1] - ๋‹ค๋ฅธ ์ผ ์ค‘...");
    }
    // ๋‹ค์Œ ์ž‘์—…
    System.out.println("[Thread 1] - ๋๋‚ฌ๊ตฐ์š”! ๊ฒฐ๊ณผ๋ฌผ์€ : \""+completableFuture.get()+"\", ์ด์ œ ๋‹ค์Œ ์ž‘์—… ์ˆ˜ํ–‰ํ• ๊ฒŒ์š”");
    System.out.println("[Thread 1] ๋‹ค์Œ ์ž‘์—… ์ˆ˜ํ–‰ ์ค‘...");

    t.destroy();
}
  • ๊ฒฐ๊ณผ
[Thread 1] - ์ž‘์—… ์‹œ์ž‘ํ• ๊ฒŒ์š”
[Thread 2] - [Thread 1]์œผ๋กœ๋ถ€ํ„ฐ ์ „๋‹ฌ๋ฐ›์€ ์ž‘์—… ์ฒ˜๋ฆฌํ• ๊ฒŒ์š”
[Thread 1] - Thread 2๋‹˜ ์ž‘์—…์ด ๋๋‚ฌ๋‚˜์š”? ๊ทธ๋™์•ˆ ์ €๋Š” ๋‹ค๋ฅธ์ผ ์ข€ ํ• ๊ฒŒ์š”
[Thread 1] - ๋‹ค๋ฅธ ์ผ ์ค‘...
[Thread 1] - Thread 2๋‹˜ ์ž‘์—…์ด ๋๋‚ฌ๋‚˜์š”? ๊ทธ๋™์•ˆ ์ €๋Š” ๋‹ค๋ฅธ์ผ ์ข€ ํ• ๊ฒŒ์š”
[Thread 1] - ๋‹ค๋ฅธ ์ผ ์ค‘...
[Thread 1] - Thread 2๋‹˜ ์ž‘์—…์ด ๋๋‚ฌ๋‚˜์š”? ๊ทธ๋™์•ˆ ์ €๋Š” ๋‹ค๋ฅธ์ผ ์ข€ ํ• ๊ฒŒ์š”
[Thread 1] - ๋‹ค๋ฅธ ์ผ ์ค‘...
[Thread 1] - ๋๋‚ฌ๊ตฐ์š”! ๊ฒฐ๊ณผ๋ฌผ์€ : "Thread 2์˜ ๊ฒฐ๊ณผ๋ฌผ", ์ด์ œ ๋‹ค์Œ ์ž‘์—… ์ˆ˜ํ–‰ํ• ๊ฒŒ์š”
[Thread 1] ๋‹ค์Œ ์ž‘์—… ์ˆ˜ํ–‰ ์ค‘...