CHAP16 - Modern-Java-in-Action/Online-Study GitHub Wiki

Chapter 16 CompletableFuture : ์•ˆ์ •์  ๋น„๋™๊ธฐ ํ”„๋กœ๊ทธ๋ž˜๋ฐ

์ด์žฅ์˜ ๋‚ด์šฉ

  • ๋น„๋™๊ธฐ ์ž‘์—…์„ ๋งŒ๋“ค๊ณ  ๊ฒฐ๊ณผ ์–ป๊ธฐ
  • ๋น„๋ธ”๋ก ๋™์ž‘์œผ๋กœ ์ƒ์‚ฐ์„ฑ ๋†’์ด๊ธฐ
  • ๋น„๋™๊ธฐ API ์„ค๊ณ„์™€ ๊ตฌํ˜„
  • ๋™๊ธฐ API๋ฅผ ๋น„๋™๊ธฐ์ ์œผ๋กœ ์†Œ๋น„ํ•˜๊ธฐ
  • ๋‘ ๊ฐœ ์ด์ƒ์˜ ๋น„๋™๊ธฐ ์—ฐ์‚ฐ์„ ํŒŒ์ดํ”„๋ผ์ธ์œผ๋กœ ๋งŒ๋“ค๊ณ  ํ•ฉ์น˜๊ธฐ
  • ๋น„๋™๊ธฐ ์ž‘์—… ์™„๋ฃŒ์— ๋Œ€์‘ํ•˜๊ธฐ

16.1 Future์˜ ๋‹จ์ˆœ ํ™œ์šฉ

Java5 -> Future

  • ์‹œ๊ฐ„์ด ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ์ž‘์—…์„ Callable ๊ฐ์ฒด ๋‚ด๋ถ€๋กœ ๊ฐ์‹ผ ๋‹ค์Œ ExecutorService์— ์ œ์ถœ
    • ExecutorService์—์„œ ์ œ๊ณตํ•˜๋Š” ์Šค๋ ˆ๋“œ๊ฐ€ ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๋™์•ˆ ์šฐ๋ฆฌ ์Šค๋ ˆ๋“œ๋กœ ๋‹ค๋ฅธ ์ž‘์—…์„ ๋™์‹œ์— ์‹คํ–‰
    • get ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•œ ๊ฒฐ๊ณผ ํšŒ์ˆ˜
    • ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ์ž‘์—…์ด ์˜์›ํžˆ ๋๋‚˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Œ -> ์ตœ๋Œ€ ํƒ€์ž„์•„์›ƒ ์‹œ๊ฐ„ ์„ค์ •

  • Future๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๋ฉ”์„œ๋“œ : ๋น„๋™๊ธฐ ๊ณ„์‚ฐ์ด ๋๋‚ฌ๋Š”์ง€ ํ™•์ธ, ๊ณ„์‚ฐ์ด ๋๋‚˜๊ธธ ๊ธฐ๋‹ค๋ฆผ, ๊ฒฐ๊ณผ ํšŒ์ˆ˜
    • ์—ฌ๋Ÿฌ Future ๊ฒฐ๊ณผ๊ฐ€ ์žˆ์„๋•Œ ์˜์กด์„ฑ์„ ํ‘œํ˜„ํ•˜๊ธฐ ์–ด๋ ค์›€
  • Stream๊ณผ ๋น„์Šทํ•˜๊ฒŒ ๋žŒ๋‹คํ‘œํ˜„์‹๊ณผ ํŒŒ์ดํ”„๋ผ์ด๋‹์„ ํ™œ์šฉํ•จ. Future์™€ CompletableFuture๋Š” Collection๊ณผ Stream์˜ ๊ด€๊ณ„์— ๋น„์œ ํ•  ์ˆ˜ ์žˆ๋‹ค.

16.1.2 CompletableFuture๋กœ ๋น„๋™๊ธฐ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋งŒ๋“ค๊ธฐ

  • ์—ฌ๋Ÿฌ ์˜จ๋ผ์ธ์ƒ์  ์ค‘ ๊ฐ€์žฅ ์ €๋ ดํ•œ ๊ฐ€๊ฒฉ์„ ์ œ์‹œํ•˜๋Š” ์ƒ์ ์„ ์ฐพ๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์˜ˆ์ œ
  • ๋ฐฐ์šธ ์ˆ˜ ์žˆ๋Š” ๊ฒƒ๋“ค
    1. ๊ณ ๊ฐ์—๊ฒŒ ๋น„๋™๊ธฐ API๋ฅผ ์ œ๊ณตํ•˜๋Š” ๋ฐฉ๋ฒ•
    2. ๋™๊ธฐ API๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ์ฝ”๋“œ๋ฅผ ๋น„๋ธ”๋ก์œผ๋กœ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•
      (๋‘ ๊ฐœ์˜ ๋น„๋™๊ธฐ ๋™์ž‘์„ ํŒŒ์ดํ”„๋ผ์ธ์œผ๋กœ ๋งŒ๋“ค๊ณ , ๋‘ ๊ฐœ์˜ ๋™์ž‘ ๊ฒฐ๊ณผ๋ฅผ ํ•˜๋‚˜์˜ ๋น„๋™๊ธฐ ๊ณ„์‚ฐ์œผ๋กœ ํ•ฉ์น˜๊ธฐ)
    3. ๋น„๋™๊ธฐ ๋™์ž‘์˜ ์™„๋ฃŒ์— ๋Œ€์‘ํ•˜๋Š” ๋ฐฉ๋ฒ•

๋™๊ธฐ API์™€ ๋น„๋™๊ธฐ API

  • ๋™๊ธฐ API : ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•œ ๋‹ค์Œ ๊ณ„์‚ฐ์„ ์™„๋ฃŒํ• ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ ธ๋‹ค๊ฐ€ ๋ฐ˜ํ™˜๋˜๋ฉด ๋‹ค๋ฅธ ๋™์ž‘์„ ์ˆ˜ํ–‰. ๋™๊ธฐ API๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ƒํ™ฉ์„ ๋ธ”๋ก ํ˜ธ์ถœ ์ด๋ผ๊ณ  ํ•จ
  • ๋น„๋™๊ธฐ API : ๋ฉ”์„œ๋“œ๋Š” ์ฆ‰์‹œ ๋ฐ˜ํ™˜๋˜๋ฉฐ ๋๋‚˜์ง€ ๋ชปํ•œ ๋‚˜๋จธ์ง€ ์ž‘์—…์€ ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ์— ํ• ๋‹นํ•จ. ๋น„๋™๊ธฐ API๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ƒํ™ฉ์„ ๋น„๋ธ”๋ก ํ˜ธ์ถœ ์ด๋ผ๊ณ  ํ•จ

16.2 ๋น„๋™๊ธฐ API ๊ตฌํ˜„

  • ์ œํ’ˆ๋ช…์— ํ•ด๋‹นํ•˜๋Š” ๊ฐ€๊ฒฉ์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฉ”์„œ๋“œ ๊ตฌํ˜„(Shop.java)
public class Shop {
    // getPrice : ๊ฐ€๊ฒฉ ์ •๋ณด๋ฅผ ์–ป๋Š” ๋™์‹œ์— ๋‹ค๋ฅธ ์™ธ๋ถ€ ์„œ๋น„์Šค์— ์ ‘๊ทผํ•˜๋Š” ๋ฉ”์„œ๋“œ
    public double getPrice(String product) {
        return calculatePrice(product);
    }

    public double calculatePrice(String product) {
        delay();
        return format(random.nextDouble() * product.charAt(0) + product.charAt(1));
    }
    
    // delay : ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ์ž‘์—…์„ ํ‰๋‚ด๋‚ด๋Š” ๋ฉ”์„œ๋“œ
    public static void delay() {
      try {
          Thread.sleep(1000L);
      } catch (InterruptedException e) {
          e.printStackTrace();
      }
  }
}

16.2.1 ๋™๊ธฐ ๋ฉ”์„œ๋“œ๋ฅผ ๋น„๋™๊ธฐ ๋ฉ”์„œ๋“œ๋กœ ๋ณ€ํ™˜

  • ์œ„ API๋ฅผ ์‚ฌ์šฉ์ž๊ฐ€ ํ˜ธ์ถœํ•˜๋Š” ๊ฒฝ์šฐ ๋น„๋™๊ธฐ ๋™์ž‘์ด ์™„๋ฃŒ๋ ๋•Œ๊นŒ์ง€ 1์ดˆ ๋™์•ˆ ๋ธ”๋ก๋จ
  • ๋™๊ธฐ ๋ฉ”์†Œ๋“œ getPrice๋ฅผ ๋น„๋™๊ธฐ ๋ฉ”์„œ๋“œ๋กœ ๋ณ€ํ™˜ํ•˜๊ธฐ(AsyncShop.java)
  • java.util.concurrent.Future : ๋น„๋™๊ธฐ ๊ณ„์‚ฐ์˜ ๊ฒฐ๊ณผ๋ฅผ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ๋Š” ์ธํ„ฐํŽ˜์ด์Šค. ๊ณ„์‚ฐ์ด ์™„๋ฃŒ๋˜๋ฉด get ๋ฉ”์„œ๋“œ๋กœ ๊ฒฐ๊ณผ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค
public class AsyncShop {
    // getPriceAsync : ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ ์ฆ‰์‹œ ๋ฐ˜ํ™˜๋˜์–ด ํ˜ธ์ถœ์ž ์Šค๋ ˆ๋“œ๊ฐ€ ๋‹ค๋ฅธ ์ž‘์—…์„ ์ˆ˜ํ–‰
    public Future<Double> getPriceAsync(String product) {
        CompletableFuture<Double> futurePrice = new CompletableFuture<>();
        new Thread(() -> {
              double price = calculatePrice(product);
              futurePrice.complete(price);
        }).start();
        return futurePrice;
    }
}

16.2.2 ์—๋Ÿฌ ์ฒ˜๋ฆฌ ๋ฐฉ๋ฒ•

  • ๊ฐ€๊ฒฉ์„ ๊ณ„์‚ฐํ•˜๋Š” ๋™์•ˆ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด get ๋ฉ”์„œ๋“œ๊ฐ€ ๋ฐ˜ํ™˜๋  ๋•Œ๊นŒ์ง€ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๊ธฐ๋‹ค๋ฆด ์ˆ˜๋„ ์žˆ์Œ
  • ๋ธ”๋ก ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์ƒํ™ฉ์—์„œ ํƒ€์ž„์•„์›ƒ์„ ํ™œ์šฉ -> ์—๋Ÿฌ ์›์ธ์„ ์•Œ ์ˆ˜ ์—†์Œ
  • completeExceptionally ๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉ -> ์—๋Ÿฌ ์›์ธ์„ ์•Œ ์ˆ˜ ์žˆ์Œ
public class AsyncShop {
    // completeExceptionally : ๋„์ค‘์— ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ๋ฐœ์ƒํ•œ ์—๋Ÿฌ๋ฅผ ํฌํ•จ์‹œ์ผœ Future๋ฅผ ์ข…๋ฃŒ
    public Future<Double> getPriceAsync(String product) {
        CompletableFuture<Double> futurePrice = new CompletableFuture<>();
        new Thread(() -> {
            try {
                double price = calculatePrice(product);
                futurePrice.complete(price);
            } catch (Exception ex) {
                futurePrice.completeExceptionally(ex);
            }
        }).start();
        return futurePrice;
    }
}

ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ supplyAsync๋กœ CompletableFuture ๋งŒ๋“ค๊ธฐ

  • Supplier๋ฅผ ์‹คํ–‰ํ•ด์„œ ๋น„๋™๊ธฐ์ ์œผ๋กœ ๊ฒฐ๊ณผ๋ฅผ ์ƒ์„ฑ
public class AsyncShop {
    public Future<Double> getPriceAsync(String product) {
        return CompletableFuture.supplyAsync(() -> calculatePrice(product));
    }
}

16.3 ๋น„๋ธ”๋ก ์ฝ”๋“œ ๋งŒ๋“ค๊ธฐ

  • 16.2์˜ ๋™๊ธฐ API๋ฅผ ์ด์šฉํ•ด์„œ ์ตœ์ €๊ฐ€๊ฒฉ ๊ฒ€์ƒ‰ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๊ฐœ๋ฐœํ•˜๋Š” ์ƒํ™ฉ์„ ๊ฐ€์ •ํ•ด๋ณด์ž
  • ์ œํ’ˆ๋ช…์„ ์ž…๋ ฅํ•˜๋ฉด ์ƒ์  ์ด๋ฆ„๊ณผ ์ œํ’ˆ๊ฐ€๊ฒฉ ๋ฌธ์ž์—ด ์ •๋ณด๋ฅผ ํฌํ•จํ•˜๋Š” List๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฉ”์„œ๋“œ ๊ตฌํ˜„(BestPriceFinder.java)
  • ์ƒ์ ์—์„œ ๊ฐ€๊ฒฉ์„ ๊ฒ€์ƒ‰ํ•˜๋Š” ๋™์•ˆ ๊ฐ๊ฐ 1์ดˆ์˜ ๋Œ€๊ธฐ์‹œ๊ฐ„ ๋ฐœ์ƒ
public class BestPriceFinder {
    private final List<Shop> shops = Arrays.asList(
            new Shop("BestPrice"),
            new Shop("LetsSaveBig"),
            new Shop("MyFavoriteShop"),
            new Shop("BuyItAll"),

    public List<String> findPricesSequential(String product) {
        return shops.stream()
                .map(shop -> String.format("%s price is %.2f", shop.getName(), shop.getPrice(product)))
                .collect(Collectors.toList());
    }
}

16.3.1 ๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ์œผ๋กœ ์š”์ฒญ ๋ณ‘๋ ฌํ™”ํ•˜๊ธฐ

  • parallelStream์„ ์ด์šฉํ•ด์„œ ์ˆœ์ฐจ ๊ณ„์‚ฐ์„ ๋ณ‘๋ ฌ๋กœ ์ฒ˜๋ฆฌํ•˜์—ฌ ์„ฑ๋Šฅ์„ ๊ฐœ์„ 

16.3.2 CompletableFuture๋กœ ๋น„๋™๊ธฐ ํ˜ธ์ถœ ๊ตฌํ˜„ํ•˜๊ธฐ

  • ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ supplyAsync๋กœ CompletableFuture ๋งŒ๋“ค๊ธฐ
  • CompletableFuture์˜ join ๋ฉ”์„œ๋“œ๋Š” Future ์ธํ„ฐํŽ˜์ด์Šค์˜ get ๋ฉ”์„œ๋“œ์™€ ๊ฐ™์€ ์˜๋ฏธ๋กœ ๋ชจ๋“  ๋น„๋™๊ธฐ ๋™์ž‘์ด ๋๋‚˜๋ฉด ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜
  • join ์‚ฌ์šฉ์‹œ ์•„๋ฌด ์˜ˆ์™ธ๋„ ๋ฐœ์ƒํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ try/catch ๋ถˆํ•„์š”
public class BestPriceFinder {
    public List<String> findPricesFuture(String product) {
        List<CompletableFuture<String>> priceFutures =
                shops.stream()
                        .map(shop -> CompletableFuture.supplyAsync(() -> shop.getName() + " price is " + shop.getPrice(product)))
                        .collect(Collectors.toList());

        return priceFutures.stream()
                .map(CompletableFuture::join)
                .collect(Collectors.toList());
    }
}
  • ์ŠคํŠธ๋ฆผ ์—ฐ์‚ฐ์€ ๊ฒŒ์œผ๋ฅธ ํŠน์„ฑ์ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋‘ ๊ฐœ์˜ ํŒŒ์ดํ”„๋ผ์ธ์œผ๋กœ ์—ฐ์‚ฐ์„ ๋‚˜๋ˆ„์–ด ์ฒ˜๋ฆฌ

16.3.3 ๋” ํ™•์žฅ์„ฑ์ด ์ข‹์€ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•

  • ์Šค๋ ˆ๋“œ ๊ฐฏ์ˆ˜๋ฅผ ์ตœ๋Œ€๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ๊นŒ์ง€๋Š” ๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ๊ณผ CompletableFuture ๊ฒฐ๊ณผ๊ฐ€ ๋น„์Šทํ•  ์ˆ˜ ์žˆ์œผ๋‚˜ ๊ทธ ์ด์ƒ์ธ ๊ฒฝ์šฐ CompletableFuture ์‚ฌ์šฉ์‹œ ๋‹ค์–‘ํ•œ Executor ์ง€์ •์œผ๋กœ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ตœ์ ํ™” ๊ฐ€๋Šฅ

16.3.4 ์ปค์Šคํ…€ Executor ์‚ฌ์šฉํ•˜๊ธฐ

  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์‹ค์ œ๋กœ ํ•„์š”ํ•œ ์ž‘์—…๋Ÿ‰์„ ๊ณ ๋ คํ•œ ํ’€์—์„œ ๊ด€๋ฆฌํ•˜๋Š” ์Šค๋ ˆ๋“œ ์ˆ˜์— ๋งž๊ฒŒ Executor ๋งŒ๋“ค๊ธฐ
public class BestPriceFinder {
    private final Executor executor = Executors.newFixedThreadPool(Math.min(shops.size(), 100), new ThreadFactory() {
                public Thread newThread(Runnable r) {
                    Thread t = new Thread(r);
                    t.setDaemon(true);          // ๋ฐ๋ชฌ ์Šค๋ ˆ๋“œ : ์ž๋ฐ” ํ”„๋กœ๊ทธ๋žจ ์ข…๋ฃŒ์‹œ ๊ฐ•์ œ๋กœ ์‹คํ–‰ ์ข…๋ฃŒ
                    return t;
                }
            });
}
  CompletableFuture.supplyAsync(() -> shop.getName() + " price is " + shop.getPrice(product), executor);
  • ๋น„๋™๊ธฐ ๋™์ž‘์„ ๋งŽ์ด ์‚ฌ์šฉํ•˜๋Š” ์ƒํ™ฉ์—์„œ๋Š” Executor๋ฅผ ๋งŒ๋“ค์–ด CompletableFuture๋ฅผ ํ™œ์šฉํ•˜๋Š” ๊ฒƒ์ด ๋ฐ”๋žŒ์ง
  • ๋‹ค์Œ์„ ์ฐธ๊ณ ํ•˜๋ฉด ์–ด๋–ค ๋ณ‘๋ ฌํ™” ๊ธฐ๋ฒ•์„ ์‚ฌ์šฉํ•  ๊ฒƒ์ธ์ง€ ์„ ํƒํ•˜๋Š”๋ฐ ๋„์›€์ด ๋œ๋‹ค
    • I/O๊ฐ€ ํฌํ•จ๋˜์ง€ ์•Š์€ ๊ณ„์‚ฐ ์ค‘์‹ฌ์˜ ๋™์ž‘์„ ์‹คํ–‰ํ•  ๋•Œ๋Š” ์ŠคํŠธ๋ฆผ ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ๊ฐ€์žฅ ๊ตฌํ˜„ํ•˜๊ธฐ ๊ฐ„๋‹จํ•˜๋ฉฐ ํšจ์œจ์ ์ผ ์ˆ˜ ์žˆ๋‹ค
    • I/O๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๋Š” ์ž‘์—…์„ ๋ณ‘๋ ฌ๋กœ ์‹คํ–‰ํ•  ๋•Œ๋Š” CompletableFuture๊ฐ€ ๋” ๋งŽ์€ ์œ ์—ฐ์„ฑ์„ ์ œ๊ณตํ•œ๋‹ค. ์ŠคํŠธ๋ฆผ์—์„œ I/O๋ฅผ ์‹ค์ œ๋กœ ์–ธ์ œ ์ฒ˜๋ฆฌํ• ์ง€ ์˜ˆ์ธกํ•˜๊ธฐ ์–ด๋ ค์šด ๋ฌธ์ œ๋„ ์žˆ๋‹ค.

16.4 ๋น„๋™๊ธฐ ์ž‘์—… ํŒŒ์ดํ”„๋ผ์ธ ๋งŒ๋“ค๊ธฐ

  • ์„ ์–ธํ˜•์œผ๋กœ ์—ฌ๋Ÿฌ ๋น„๋™๊ธฐ ์—ฐ์‚ฐ์„ CompletableFuture๋กœ ํŒŒ์ดํ”„๋ผ์ธํ™” ํ•ด๋ณด์ž
  • ๊ณ„์•ฝ์„ ๋งบ์€ ๋ชจ๋“  ์ƒ์ ์ด ํ•˜๋‚˜์˜ ํ• ์ธ ์„œ๋น„์Šค๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ๋กœ ํ•˜์˜€์œผ๋ฉฐ(Discount.java)
  • ์ƒ์ ์—์„œ getPrice์˜ ๊ฒฐ๊ณผ ํ˜•์‹๋„ ๋ณ€๊ฒฝํ•˜์˜€๋‹ค(Shop.java)
public class Shop {
    public String getPrice(String product) {
        double price = calculatePrice(product);
        Discount.Code code = Discount.Code.values()[random.nextInt(Discount.Code.values().length)];
        return name + ":" + price + ":" + code;
    }

    public double calculatePrice(String product) {
        delay();
        return format(random.nextDouble() * product.charAt(0) + product.charAt(1));
    }
}

16.4.1 ํ• ์ธ ์„œ๋น„์Šค ๊ตฌํ˜„

  • ์ƒ์ ์—์„œ ์ œ๊ณตํ•œ ๋ฌธ์ž์—ด ํŒŒ์‹ฑ์„ Quote ํด๋ž˜์Šค๋กœ ์บก์Аํ™”(Quote.java)

16.4.2 ํ• ์ธ ์„œ๋น„์Šค ์‚ฌ์šฉ

  • Discount๋Š” ์›๊ฒฉ ์„œ๋น„์Šค์ด๋ฏ€๋กœ 1์ดˆ์˜ ์ง€์—ฐ์„ ์ถ”๊ฐ€ํ•จ(delay)
  • ํ• ์ธ ์ „ ๊ฐ€๊ฒฉ์„ ์–ป์€ ํ›„ -> ์ƒ์ ์—์„œ ๋ฐ˜ํ™˜๋œ ๋ฌธ์ž์—ด์„ Quote ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜ ํ›„ -> Discount ์„œ๋น„์Šค๋ฅผ ์ด์šฉํ•˜์—ฌ ํ• ์ธ์„ ์ ์šฉ
public List<String> findPrices(String product) {
    return shops.stream()
            .map(shop -> shop.getPrice(product))
            .map(Quote::parse)
            .map(Discount::applyDiscount)
            .collect(Collectors.toList());
}

16.4.3 ๋™๊ธฐ ์ž‘์—…๊ณผ ๋น„๋™๊ธฐ ์ž‘์—… ์กฐํ•ฉํ•˜๊ธฐ

  • CompletableFuture ์—์„œ ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋Šฅ์œผ๋กœ findPrices ๋ฉ”์„œ๋“œ๋ฅผ ๋น„๋™๊ธฐ์ ์œผ๋กœ ์žฌ๊ตฌํ˜„
public List<String> findPricesFuture(String product) {
    List<CompletableFuture<String>> priceFutures =
            shops.stream()
                    .map(shop -> CompletableFuture.supplyAsync(() -> shop.getPrice(product), executor))
                    .map(future -> future.thenApply(Quote::parse))
                    .map(future -> future.thenCompose(quote -> CompletableFuture.supplyAsync(() -> Discount.applyDiscount(quote), executor)))
                    .collect(Collectors.toList());

    return priceFutures.stream()
            .map(CompletableFuture::join)
            .collect(Collectors.toList());
}

๊ฐ€๊ฒฉ ์ •๋ณด ์–ป๊ธฐ

  • supplyAsync์— ๋‹ฎ๋‹ค ํ‘œํ˜„์‹์„ ์ „๋‹ฌํ•˜์—ฌ ๋น„๋™๊ธฐ์ ์œผ๋กœ ์ƒ์ ์—์„œ ์ •๋ณด๋ฅผ ์กฐํšŒ
  • ์ฒซ ๋ฒˆ์งธ ๋ณ€ํ™˜์˜ ๋ฐ˜ํ™˜ ๊ฒฐ๊ณผ๋Š” Stream<CompletableFuture<String>>
  • ๊ฐ CompletableFuture๋Š” ์ž‘์—…์ด ๋๋‚ฌ์„ ๋•Œ ํ•ด๋‹น ์ƒ์ ์—์„œ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฌธ์ž์—ด ์ •๋ณด ํฌํ•จ
  • ์ปค์Šคํ…€ executor ์‚ฌ์šฉ

Quote ํŒŒ์‹ฑํ•˜๊ธฐ

  • ์ฒซ ๋ฒˆ์งธ ๊ฒฐ๊ณผ ๋ฌธ์ž์—ด์„ Quote๋กœ ๋ณ€ํ™˜ -> ์›๊ฒฉ ์„œ๋น„์Šค๋‚˜ I/O๊ฐ€ ์—†์œผ๋ฏ€๋กœ ์ง€์—ฐ ์—†์ด ๋™์ž‘ ์ˆ˜ํ–‰ ๊ฐ€๋Šฅ
  • thenApply ๋ฉ”์„œ๋“œ๋Š” CompletableFuture๊ฐ€ ๋๋‚ ๋•Œ๊นŒ์ง€ ๋ธ”๋กํ•˜์ง€ ์•Š์Œ
  • CompletableFuture๊ฐ€ ๋™์ž‘์„ ์™„์ „ํžˆ ์™„๋ฃŒํ•œ ๋‹ค์Œ์— thenApply ๋ฉ”์„œ๋“œ๋กœ ์ „๋‹ฌ๋œ ๋žŒ๋‹ค ํ‘œํ˜„์‹์„ ์ ์šฉ

CompletableFuture๋ฅผ ์กฐํ•ฉํ•˜์—ฌ ํ• ์ธ๋œ ๊ฐ€๊ฒฉ ๊ณ„์‚ฐํ•˜๊ธฐ

  • ์ฒซ ๋ฒˆ์งธ map์—์„œ ๋ฐ›์€ ํ• ์ธ์ „ ๊ฐ€๊ฒฉ์— Discount๋กœ ํ• ์ธ๋œ ๊ฐ€๊ฒฉ์„ ๊ณ„์‚ฐ
  • ์›๊ฒฉ ์‹คํ–‰์ด ํฌํ•จ๋˜์–ด ๋™๊ธฐ์ ์œผ๋กœ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ด์•ผ ํ•จ -> ๋žŒ๋‹คํ‘œํ˜„์‹์œผ๋กœ supplyAsync์— ์ „๋‹ฌํ•˜์—ฌ ๋‹ค๋ฅธ CompletableFuture๋ฅผ ๋ฐ˜ํ™˜ ๊ฐ€๋Šฅ
  • ๋‘ ๊ฐ€์ง€ CompletableFuture๋กœ ์ด๋ฃจ์–ด์ง„ ์—ฐ์‡„์ ์œผ๋กœ ์ˆ˜ํ–‰๋˜๋Š” ๋น„๋™๊ธฐ ๋™์ž‘์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.
    • ์ƒ์ ์—์„œ ๊ฐ€๊ฒฉ ์ •๋ณด๋ฅผ ์–ป์–ด์™€์„œ Quote๋กœ ๋ณ€ํ™˜
    • ๋ณ€ํ™˜๋œ Quote๋ฅผ Discount ์„œ๋น„์Šค๋กœ ์ „๋‹ฌํ•ด์„œ ํ• ์ธ๋œ ์ตœ์ข…๊ฐ€๊ฒฉ ํš๋“
  • thenCompose ๋ฉ”์„œ๋“œ๋Š” ์ฒซ ๋ฒˆ์งธ ์—ฐ์‚ฐ์˜ ๊ฒฐ๊ณผ๋ฅผ ๋‘ ๋ฒˆ์งธ ์—ฐ์‚ฐ์œผ๋กœ ์ „๋‹ฌ

16.4.4 ๋…๋ฆฝ CompletableFuture์™€ ๋น„๋…๋ฆฝ CompletableFuture ํ•ฉ์น˜๊ธฐ

  • ์‹ค์ „์—์„œ๋Š” ๋…๋ฆฝ์ ์œผ๋กœ ์‹คํ–‰๋œ ๋‘ ๊ฐœ์˜ CompletableFuture ๊ฒฐ๊ณผ๋ฅผ ํ•ฉ์ณ์•ผ ํ•˜๋Š” ์ƒํ™ฉ์ด ์ข…์ข… ๋ฐœ์ƒํ•จ
  • thenCombine ๋ฉ”์„œ๋“œ๋Š” BiFunction์„ ๋‘ ๋ฒˆ์งธ ์ธ์ˆ˜๋กœ ๋ฐ›์•„ ๋‘ ๊ฐœ์˜ CompletableFuture ๊ฒฐ๊ณผ๋ฅผ ํ•ฉ์นœ๋‹ค
  • thenCombineAsync ๋ฉ”์„œ๋“œ๋Š” thenCombine์˜ Async ๋ฒ„์ „์œผ๋กœ BiFunction์ด ์ •์˜ํ•˜๋Š” ์กฐํ•ฉ ๋™์ž‘์ด ์Šค๋ ˆ๋“œ ํ’€๋กœ ์ œ์ถœ๋˜๋ฉด์„œ ๋ณ„๋„์˜ ํƒœ์Šคํฌ์—์„œ ๋น„๋™๊ธฐ์ ์œผ๋กœ ์ˆ˜ํ–‰
  • ์œ ๋กœ(EUR) ๊ฐ€๊ฒฉ ์ •๋ณด๋ฅผ ์ œ๊ณตํ•˜๋Š” ์˜จ๋ผ์ธ์ƒ์ ์—์„œ ๋‹ฌ๋Ÿฌ(USD) ๊ฐ€๊ฒฉ์œผ๋กœ ๋ณ€๊ฒฝํ•˜์—ฌ ๋ณด์—ฌ์ฃผ๊ธฐ

16.4.5 Future์˜ ๋ฆฌํ”Œ๋ ‰์…˜๊ณผ CompletableFuture์˜ ๋ฆฌํ”Œ๋ ‰์…˜

  • Future์™€ ExecutorService ์‚ฌ์šฉ์‹œ ์Šค๋ ˆ๋“œ ๋ฐ˜ํ™˜ ํ•„์ˆ˜

16.4.6 ํƒ€์ž„์•„์›ƒ ํšจ๊ณผ์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๊ธฐ

  • Future์˜ ๊ณ„์‚ฐ ๊ฒฐ๊ณผ๋ฅผ ์ฝ์„ ๋•Œ๋Š” ๋ฌดํ•œ์ • ๊ธฐ๋‹ค๋ฆฌ๋Š” ์ƒํ™ฉ์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ๋ธ”๋ก์„ ํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ด ์ข‹๋‹ค
  • ์ž๋ฐ”9์—์„œ๋Š” CompletableFuture์—์„œ ์ œ๊ณตํ•˜๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉํ•ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค
    • orTimeout : ์ง€์ •๋œ ์‹œ๊ฐ„์ด ์ง€๋‚œ ํ›„ CompletableFuture๋ฅผ TimeoutException์œผ๋กœ ์™„๋ฃŒํ•˜๋ฉด์„œ ๋˜ ๋‹ค๋ฅธ CompletableFuture๋ฅผ ๋ฐ˜ํ™˜(ScheduledThreadExecutor ํ™œ์šฉ)
    • completeOnTimeout : ์ž‘์—…์ด ์™„๋ฃŒ๋˜์ง€ ์•Š์œผ๋ฉด ๋ฏธ๋ฆฌ ์ง€์ •๋œ ๊ฐ’์„ ์‚ฌ์šฉํ•˜๋„๋ก ํ•จ

16.5 CompletableFuture์˜ ์ข…๋ฃŒ์— ๋Œ€์‘ํ•˜๋Š” ๋ฐฉ๋ฒ•

  • ์›๊ฒฉ ๋ฉ”์„œ๋“œ ์‘๋‹ต์„ 1์ดˆ์˜ ์ง€์—ฐ์œผ๋กœ ๊ณ ์ • -> ์‹ค์ „์—์„œ๋Š” ์–ผ๋งˆ๋‚˜ ์ง€์—ฐ๋ ์ง€ ์˜ˆ์ธกํ•˜๊ธฐ ์–ด๋ ต๋‹ค
  • delay๋Œ€์‹  0.5 ~ 2.5 ์‚ฌ์ด์˜ ์ง€์—ฐ์„ ๋งŒ๋“œ๋Š” randomDelay ์‚ฌ์šฉ

16.5.1 ์ตœ์ €๊ฐ€๊ฒฉ ๊ฒ€์ƒ‰ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ฆฌํŒฉํ„ฐ๋ง

  • ๊ฐ ์ƒ์ ์—์„œ ๊ฐ€๊ฒฉ ์ •๋ณด๋ฅผ ์ œ๊ณตํ•  ๋•Œ๋งˆ๋‹ค ์ฆ‰์‹œ ๋ณด์—ฌ์ค„ ์ˆ˜ ์žˆ๋Š” ์ตœ์ €๊ฐ€๊ฒฉ ๊ฒ€์ƒ‰ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜
  • thenAccept : CompletableFuture๊ฐ€ ์ƒ์„ฑํ•œ ๊ฒฐ๊ณผ๋ฅผ ์–ด๋–ป๊ฒŒ ์†Œ๋น„ํ• ์ง€ ๋ฏธ๋ฆฌ ์ง€์ •
  • allOf : CompletableFuture ๋ฐฐ์—ด์„ ์ž…๋ ฅ์œผ๋กœ ๋ฐ›์•„ ๋ชจ๋‘ ์™„๋ฃŒ๋˜์—ˆ์„ ๋•Œ CompletableFuture<Void>๋ฅผ ๋ฐ˜ํ™˜
  • anyOf : CompletableFuture ๋ฐฐ์—ด์„ ์ž…๋ ฅ์œผ๋กœ ๋ฐ›์•„ ํ•˜๋‚˜๋ผ๋„ ์ž‘์—…์ด ๋๋‚ฌ์„ ๋•Œ ์™„๋ฃŒํ•œ CompletableFuture<Object>๋ฅผ ๋ฐ˜ํ™˜

16.7 ๋งˆ์น˜๋ฉฐ

  • ํ•œ ๊ฐœ ์ด์ƒ์˜ ์›๊ฒฉ ์™ธ๋ถ€ ์„œ๋น„์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ธด ๋™์ž‘์„ ์‹คํ–‰ํ•  ๋•Œ๋Š” ๋น„๋™๊ธฐ ๋ฐฉ์‹์œผ๋กœ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์„ฑ๋Šฅ๊ณผ ๋ฐ˜์‘์„ฑ์„ ํ–ฅ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค.
  • ์šฐ๋ฆฌ ๊ณ ๊ฐ์—๊ฒŒ ๋น„๋™๊ธฐ API๋ฅผ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ์„ ๊ณ ๋ คํ•ด์•ผ ํ•œ๋‹ค. CompletableFuture์˜ ๊ธฐ๋Šฅ์„ ์ด์šฉํ•˜๋ฉด ์‰ฝ๊ฒŒ ๋น„๋™๊ธฐ API๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • CompletableFuture๋ฅผ ์ด์šฉํ•  ๋•Œ ๋น„๋™๊ธฐ ํƒœ์Šคํฌ์—์„œ ๋ฐœ์ƒํ•œ ์—๋Ÿฌ๋ฅผ ๊ด€๋ฆฌํ•˜๊ณ  ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๋™๊ธฐ API๋ฅผ CompletableFuture๋กœ ๊ฐ์‹ธ์„œ ๋น„๋™๊ธฐ์ ์ด๋กœ ์†Œ๋น„ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์„œ๋กœ ๋…๋ฆฝ์ ์ธ ๋น„๋™๊ธฐ ๋™์ž‘์ด๋“  ์•„๋‹ˆ๋ฉด ํ•˜๋‚˜์˜ ๋น„๋™๊ธฐ ๋™์ž‘์ด ๋‹ค๋ฅธ ๋น„๋™๊ธฐ ๋™์ž‘์˜ ๊ฒฐ๊ณผ์— ์˜์กดํ•˜๋Š” ์ƒํ™ฉ์ด๋“  ์—ฌ๋Ÿฌ ๋น„๋™๊ธฐ ๋™์ž‘์„ ์กฐ๋ฆฝํ•˜๊ณ  ์กฐํ•ฉํ•  ์ˆ˜ ์žˆ๋‹ค.
  • CompletableFuture์— ์ฝœ๋ฐฑ์„ ๋“ฑ๋กํ•ด์„œ Future๊ฐ€ ๋™์ž‘์„ ๋๋‚ด๊ณ  ๊ฒฐ๊ณผ๋ฅผ ์ƒ์‚ฐํ–ˆ์„ ๋•Œ ์–ด๋–ค ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋„๋ก ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • CompletableFuture ๋ฆฌ์ŠคํŠธ์˜ ๋ชจ๋“  ๊ฐ’์ด ์™„๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆด์ง€ ์•„๋‹ˆ๋ฉด ์ฒซ ๊ฐ’๋งŒ ์™„๋ฃŒ๋˜๊ธธ ๊ธฐ๋‹ค๋ฆด์ง€ ์„ ํƒํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์ž๋ฐ” 9์—์„œ๋Š” orTimeout, completeOnTimeout ๋ฉ”์„œ๋“œ๋กœ CompletableFuture์— ๋น„๋™๊ธฐ ํƒ€์ž„์•„์›ƒ ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ–ˆ๋‹ค.
โš ๏ธ **GitHub.com Fallback** โš ๏ธ