Java 동시성에서 일괄 작업 동기화 및 관리

실행자 서비스

ExecutorService는 여러 비동기 작업을 실행하고 관리하기 위해 스레드 풀을 관리하고 제어하는 ​​방법을 제공하는 Java 동시성 라이브러리의 인터페이스입니다. ExecutorService를 사용하면 스레드 생성 및 관리를 직접 조작할 필요 없이 스레드 작업을 보다 편리하게 제출, 실행 및 관리할 수 있습니다.

ExecutorService 인터페이스는 작업 제출, 스레드 풀 닫기, 작업 완료 대기 등을 포함하여 스레드 풀을 작동하기 위한 몇 가지 메서드를 정의합니다. 다양한 동시성 요구 사항을 충족하기 위해 고정 크기 스레드 풀, 캐시 스레드 풀, 단일 스레드 풀 등과 같은 다양한 유형의 스레드 풀 구현을 제공합니다.

1、호출Any

**invokeAny()**는 작업 배치를 제출하고 작업 중 하나가 완료될 때까지 기다리는 데 사용됩니다(결과 반환 또는 예외 발생). 반환되는 것은 성공적으로 완료된 첫 번째 작업의 결과입니다.

이 방법은 여러 작업을 병렬로 실행해야 하지만 가장 빠르게 완료되는 작업의 결과에만 관심이 있는 시나리오에서 매우 유용합니다. 여러 작업이 완료된 경우 작업 중 하나의 결과만 반환됩니다.

다음은 InvokeAny() 메서드를 사용하는 간단한 예입니다.

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

public class InvokeAnyExample {
    
    
    public static void main(String[] args) {
    
    
        //项目中要使用自定义线程池,不要使用Executors
        ExecutorService executor = Executors.newFixedThreadPool(5);

        List<Callable<Integer>> tasks = new ArrayList<>();
        for (int i = 1; i <= 10; i++) {
    
    
            final int taskId = i;
            tasks.add(() -> {
    
    
                // 模拟不同耗时的任务
                Thread.sleep(1000);
                return taskId;
            });
        }

        try {
    
    
            int result = executor.invokeAny(tasks);

            System.out.println("First completed task result: " + result);
        } catch (InterruptedException | ExecutionException e) {
    
    
            e.printStackTrace();
        }

        executor.shutdown();
    }
}

실행 결과 :

First completed task result: 3

먼저 고정 크기 스레드 풀 실행기가 생성된 다음 여러 호출 가능 작업이 포함된 목록 작업이 생성됩니다. 각 작업은 점점 더 많은 시간이 소요되는 작업을 시뮬레이션합니다. 그런 다음 executor.invokeAny(tasks)를 사용하여 작업을 제출하고 작업 중 하나가 완료될 때까지 기다린 후 성공적으로 완료된 첫 번째 작업의 결과를 반환합니다.

InvokeAny() 메소드는 최소한 하나의 작업이 완료될 때까지 현재 스레드를 차단한다는 점에 유의해야 합니다 . 모든 작업이 실패하거나 예외가 발생하면 ExecutionException 이 발생합니다 . 실제 사용에서는 작업의 요구 사항과 성격에 따라 병렬 작업을 처리하는 적절한 방법을 선택합니다.

다음은 InvokeAll() 메서드를 사용하는 간단한 예입니다.

package com.lf.java.basic.concurrent;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

public class InvokeAllExample {
    
    
    public static void main(String[] args) {
    
    
        //项目中要使用自定义线程池,不要使用Executors
        ExecutorService executor = Executors.newFixedThreadPool(5);

        List<Callable<Integer>> tasks = new ArrayList<>();
        for (int i = 1; i <= 10; i++) {
    
    
            final int taskId = i;
            tasks.add(() -> {
    
    
                // 模拟耗时任务
                Thread.sleep(1000);
                return taskId;
            });
        }

        try {
    
    
            List<Future<Integer>> results = executor.invokeAll(tasks);

            // 处理任务结果
            for (Future<Integer> result : results) {
    
    
                try {
    
    
                    int taskId = result.get();
                    System.out.println("Task " + taskId + " completed.");
                } catch (InterruptedException | ExecutionException e) {
    
    
                    e.printStackTrace();
                }
            }
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }

        executor.shutdown();
    }
}

실행 결과 :

Task 1 completed.
Task 2 completed.
Task 3 completed.
Task 4 completed.
Task 5 completed.
Task 6 completed.
Task 7 completed.
Task 8 completed.
Task 9 completed.
Task 10 completed.

위의 예에서는 먼저 고정 크기 스레드 풀 실행기가 생성된 다음 여러 호출 가능 작업이 포함된 목록 작업이 생성됩니다. 각 작업은 시간이 많이 걸리는 작업을 시뮬레이션하고 작업의 ID를 반환합니다. 다음으로, executor.invokeAll(tasks)을 사용하여 작업을 제출하고 모든 작업이 완료될 때까지 기다렸다가 Future 개체 목록을 반환합니다.

마지막으로 결과 목록을 순회하고 Future.get() 메서드를 사용하여 각 작업의 결과를 얻은 다음 작업이 완료된 후 로직을 처리하고 추가된 작업 순서대로 결과를 반환합니다 .

InvokeAll() 메소드는 모든 작업이 완료될 때까지 현재 스레드를 차단한다는 점에 유의해야 합니다 . 작업이 완료되기 전에 중단되면 InterruptedException이 발생합니다 . 실제 사용 시에는 InvokeAll() 또는 기타 메소드를 사용하여 작업의 성격과 요구 사항에 따라 합리적으로 작업을 제출하고 처리합니다.

2, 모두 호출

InvokeAll(tasks)는 Java 동시성 라이브러리의 ExecutorService 인터페이스에서 제공하는 메서드로, 일괄 작업을 제출하고 모든 작업이 완료될 때까지 기다리는 데 사용됩니다. 반환되는 것은 Future 객체를 포함하는 목록이며, 각 Future 객체는 작업의 실행 결과를 나타냅니다.
이 방법은 일반적으로 일괄 처리, 병렬 컴퓨팅 등과 같이 여러 작업을 동시에 제출하고 모든 작업이 완료될 때까지 기다려야 하는 시나리오에서 사용됩니다.

실행자완료서비스

ExecutorCompletionService 는 비동기 작업의 실행 및 결과 획득을 관리하기 위해 Executor 기반 동시성 모드를 구현하는 Java의 유틸리티 클래스입니다 . 동시 실행을 위해 작업을 Executor에 제출할 수 있으며 해당 결과는 완료된 순서대로 검색될 수 있습니다.

다중 스레드 동시 프로그래밍에서는 ExecutorCompletionService를 사용하여 비동기 작업 그룹의 결과를 원활하게 처리할 수 있습니다. 특히 여러 작업이 완료되고 결과를 얻을 때까지 기다려야 하는 시나리오 의 경우 더욱 그렇습니다.

1、실행자 ExecutorCompletionService

샘플 코드:

package com.lf.java.basic.concurrent;

import java.util.concurrent.*;

public class ExecutorCompletionServiceExample {
    
    

    public static void main(String[] args) {
    
    
        //项目中要使用自定义线程池,不要使用Executors
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        ExecutorCompletionService<Integer> completionService = new ExecutorCompletionService<>(executorService);

        // 提交一些任务
        for (int i = 1; i <= 10; i++) {
    
    
            final int taskId = i;
            completionService.submit(() -> {
    
    
                // 模拟耗时任务
                Thread.sleep(1000);
                return taskId;
            });
        }

        // 获取任务结果
        try {
    
    
            for (int i = 1; i <= 10; i++) {
    
    
                Future<Integer> completedTask = completionService.take();
                int result = completedTask.get();
                System.out.println("Task " + result + " completed.");
            }
        } catch (InterruptedException | ExecutionException e) {
    
    
            e.printStackTrace();
        }

        // 关闭ExecutorService
        executorService.shutdown();
    }
}

작업 결과:

Task 3 completed.
Task 2 completed.
Task 1 completed.
Task 4 completed.
Task 5 completed.
Task 6 completed.
Task 7 completed.
Task 8 completed.
Task 9 completed.
Task 10 completed.

먼저 고정 크기 스레드 풀 executorService가 생성된 후 ExecutorCompletionService가 생성되고 여기에 executorService가 전달됩니다. 다음으로, 동시에 실행될 일련의 작업을 CompletionService에 제출합니다.

완료된 작업 대기열에서 완료된 작업을 가져오려면 CompletionService.take() 메서드를 사용하세요. 이 메서드는 작업이 완료될 때까지 차단 됩니다. 그러면 획득한 Future 객체를 통해 작업 결과를 얻을 수 있다.

ExecutorCompletionService는 작업이 제출된 순서가 아닌 작업이 완료된 순서대로 결과를 반환한다는 점에 유의해야 합니다 . 이렇게 하면 모든 작업이 완료될 때까지 기다리지 않고 완료된 작업의 결과를 더 쉽게 처리할 수 있습니다 .

executorService.shutdown() 이 호출 되면 스레드 풀은 새 작업 수락을 중지하고 제출되었지만 아직 실행되지 않은 작업을 완료하려고 시도합니다. 이미 실행 중인 작업은 계속되지만 새 작업은 허용되지 않습니다. 모든 작업이 완료되면 스레드 풀이 완전히 닫히고 리소스가 해제됩니다.

executorService.shutdown() 이 제거 되면 스레드 풀은 닫히지 않지만 계속해서 새 작업을 기다리고 제출된 작업을 실행합니다. 이로 인해 스레드 풀을 명시적으로 닫지 않고도 프로그램이 영원히 실행될 수 있습니다.

리소스 누수: 스레드 풀이 닫히지 않아 스레드 리소스가 해제되지 않아 시스템 리소스가 낭비될 수 있습니다.

프로그램이 정상적으로 종료되지 않습니다 . : 스레드 풀에 있는 스레드들이 항상 실행 중이기 때문에 메인 프로그램이 원활하게 종료되지 않습니다.

프로그램의 정상적인 종료와 리소스의 해제를 보장하기 위해서는 스레드 풀이 필요하지 않을 때 스레드 풀을 닫는 시간에 executorService.shutdown()을 호출하는 것이 좋습니다. 그러면 스레드 풀의 모든 스레드가 종료되고 관련 리소스가 해제됩니다. 스레드 풀을 닫기 전에 스레드 풀의 모든 작업이 실행될 때까지 기다리려면 waitTermination() 메서드를 사용하여 기다릴 수 있습니다.

2. 사용자 정의 스레드 풀 ExecutorCompletionService

사용자 정의 스레드 풀

package com.lf.java.basic.concurrent;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

@Slf4j
public class WhiteListThreadPool {
    
    

    private WhiteListThreadPool() {
    
    
    }

    public static final ExecutorService executorService = new ThreadPoolExecutor(
             //机器核数
            Runtime.getRuntime().availableProcessors() + 1,
            Runtime.getRuntime().availableProcessors() * 2 + 1,
            0L,
            TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(1000),
            new NameTreadFactory(),
            new ThreadPoolExecutor.CallerRunsPolicy()
    );

    static class NameTreadFactory implements ThreadFactory {
    
    

        private final AtomicInteger threadNum = new AtomicInteger(1);

        @Override
        public Thread newThread(Runnable r) {
    
    
            Thread t = new Thread(r, "white-list-worker" + threadNum.getAndIncrement());
            log.info(t.getName() + "create...");
            return t;
        }
    }

}


ExecutorCompletionServiceExample은 사용자 정의 스레드 풀을 사용합니다.

package com.lf.java.basic.concurrent;

import java.util.concurrent.*;

public class ExecutorCompletionServiceExample {
    
    

    public static void main(String[] args) {
    
    
        
        ExecutorService executorService = WhiteListThreadPool.executorService;
        ExecutorCompletionService<Integer> completionService = new ExecutorCompletionService<>(executorService);

        // 提交一些任务
        for (int i = 1; i <= 10; i++) {
    
    
            final int taskId = i;
            completionService.submit(() -> {
    
    
                // 模拟耗时任务
                Thread.sleep(1000);
                return taskId;
            });
        }

        // 获取任务结果
        try {
    
    
            for (int i = 1; i <= 10; i++) {
    
    
                Future<Integer> completedTask = completionService.take();
                int result = completedTask.get();
                System.out.println("Task " + result + " completed.");
            }
        } catch (InterruptedException | ExecutionException e) {
    
    
            e.printStackTrace();
        }

        // 自定义线程池一般不需要关闭ExecutorService
//        executorService.shutdown();
    }
}

완성 가능한 미래

CompletableFuture는 비동기 작업의 결과와 작업을 처리하기 위해 Java 동시성 라이브러리에 제공되는 클래스입니다. 비동기 프로그래밍을 처리하는 보다 유연하고 강력한 방법을 제공하며 복잡한 비동기 작업 구성, 변환 및 작업을 쉽게 실현할 수 있습니다.
CompletableFuture의 몇 가지 중요한 기능과 사용법은 다음과 같습니다.

  1. 비동기 작업 제출: 비동기 작업은 CompletableFuture.supplyAsync(공급업체 공급자) 또는 CompletableFuture.runAsync(Runnable runnable) 메서드를 통해
    제출될 수 있습니다 . SupplyAsync는 결과와 함께 CompletableFuture를 반환하고, runAsync는 결과 없이 CompletableFuture를 반환합니다.

  2. 연결 작업: 일련의 then 메서드를 사용하면 여러 비동기 작업을 함께 연결하여 작업 프로세스를 구성할 수 있습니다. 예를 들어,
    결과 처리를 위해 thenApply, thenAccept, thenRun을 수행하고 여러 작업을 결합하기 위해 thenCompose, thenCombine, thenCombineAsync를 수행합니다.

  3. 변환 및 매핑:
    thenApply 및 thenCompose와 같은 메소드를 통해 작업 결과를 변환하고 매핑할 수 있습니다. 이는 비동기 작업의 결과를 조작, 필터링, 변환하는 데 유용합니다.

  4. 조합 작업: thenCombine, thenCombineAsync, thenCompose 등의 메서드를 통해
    여러 작업의 결과를 결합하여 보다 복잡한 작업 프로세스를 구축할 수 있습니다.

  5. 예외 처리:Exceptionly(Function<Throwable, T> function) 및
    handler(BiFunction<T, Throwable, T> function) 메소드를 통해 작업 실행 중 예외를 처리하고 기본값을 반환하거나 처리 결과를 반환할 수 있습니다.

  6. 대기 및 결과 얻기: Join() 또는 get() 메서드를 사용하면 작업이 완료되고 결과를 얻을 때까지 기다릴 수 있습니다. Join() 메소드는 예외를 발생시키지 않지만 get() 메소드는 예외를 발생시킬 수 있습니다.

  7. 여러 작업이 완료될 때까지 대기: CompletableFuture.allOf(CompletableFuture<?>... cfs)를 사용하여 여러 작업이 완료될 때까지 기다리거나 CompletableFuture.anyOf(CompletableFuture<?>... cfs)를 사용하여 작업이 완료될 때까지 기다립니다
    . 완벽한

  8. 콜백 메커니즘:
    thenAccept, thenRun, whenComplete, 예외적으로 등과 같은 메소드를 통해 콜백을 추가하여 작업 완료, 결과 처리 및 예외를 처리할 수 있습니다.

  9. 비동기 실행: CompletableFuture는 지정된 스레드 풀을 사용하여 작업을 실행할 수 있는 다양한 비동기 실행 메서드(예:
    thenApplyAsync, thenAcceptAsync, thenRunAsync)를 제공합니다.

  10. 작업 취소: cancel(boolean mayInterruptIfRunning) 메서드를 사용하여 작업 실행을 취소해 보세요.

CompletableFuture는 복잡한 비동기 작업 프로세스 및 작업을 구축하는 데 적합한 보다 발전되고 유연한 비동기 프로그래밍 방법을 제공합니다. 강력한 기능은 코드의 가독성과 유지 관리성을 효과적으로 향상시킬 수 있지만 합리적인 예외 처리, 스레드 리소스 및 스레드 풀 관리에도 주의를 기울여야 합니다.

1. 일괄 작업 제출 및 결과 처리

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

public class CompletableFutureBatchExample {
    
    
    public static void main(String[] args) {
    
    
        List<CompletableFuture<Integer>> futures = new ArrayList<>();

        for (int i = 1; i <= 10; i++) {
    
    
            int taskId = i;
            CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
    
    
                // 模拟耗时任务
                try {
    
    
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
                return taskId;
            });
            futures.add(future);
        }

        CompletableFuture<Void> allOf = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));

        CompletableFuture<List<Integer>> resultFuture = allOf.thenApplyAsync(v ->
                futures.stream()
                        .map(CompletableFuture::join)
                        .collect(Collectors.toList())
        );

        try {
    
    
            List<Integer> results = resultFuture.get();
            System.out.println("Results: " + results);
        } catch (InterruptedException | ExecutionException e) {
    
    
            e.printStackTrace();
        }
    }
}

작업 결과:

Results: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

이 예에서는 각각 비동기 작업을 나타내는 여러 CompletableFuture 개체를 포함하는 미래 목록이 먼저 생성됩니다. 그런 다음 CompletableFuture.allOf()를 사용하여 모든 작업이 완료될 때까지 기다립니다. 다음으로 모든 작업의 ​​결과는 thenApplyAsync() 메서드를 통해 처리되어 각 작업의 결과를 목록으로 수집합니다.

2. 작업과 예외 처리 결합:

import java.util.concurrent.CompletableFuture;

public class CompletableFutureComposeExample {
    
    
    public static void main(String[] args) {
    
    
        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> 10);
        CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> 20);

        CompletableFuture<Integer> combinedFuture = future1.thenCombineAsync(future2, (result1, result2) -> result1 + result2);

        combinedFuture
                .exceptionally(ex -> {
    
    
                    System.out.println("Exception: " + ex.getMessage());
                    return 0;
                })
                .thenAccept(result -> {
    
    
                    System.out.println("Combined Result: " + result);
                });

        combinedFuture.join();
    }
}

작업 결과:

Combined Result: 30

이 예제에서는 두 개의 CompletableFuture 객체 future1future2 가 생성 되고 해당 결과가 thenCombineAsync () 메서드를 사용하여 추가됩니다 . 동시에 예외를 처리하기 위해 예외적으로 ()를 사용하고 작업 결과를 처리하기 위해 thenAccept()를 사용합니다.

이러한 유연한 운영과 연쇄 처리를 통해 더욱 복잡한 비동기 작업 프로세스를 생성하고 보다 정밀한 작업 동기화 및 관리를 달성할 수 있습니다.

구아바 ListenableFuture

ListenableFuture 는 결과를 처리하고 비동기 작업을 모니터링하기 위해 Google Guava 라이브러리에 제공되는 인터페이스입니다. 이는 특히 비동기 작업 완료 시 모니터링 및 콜백에서 더욱 강력하고 유연한 기능을 제공하는 표준 Java Future 인터페이스의 확장입니다.

ListenableFuture의 가장 큰 장점은 리스너를 등록하고 비동기 작업이 완료될 때 자동으로 콜백을 트리거하여 수동 폴링을 피하거나 작업이 완료되기를 기다리는 스레드를 차단할 수 있다는 것입니다. 이는 동시 프로그래밍 및 비동기 작업에 매우 유용하며 성공 및 실패 처리를 사용자 정의할 수 있습니다 .

코드 예:

import com.google.common.util.concurrent.*;

import java.util.concurrent.Executors;

public class ListenableFutureExample {
    
    
    public static void main(String[] args) {
    
    
        ListeningExecutorService executorService = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(5));

        ListenableFuture<Integer> future = executorService.submit(() -> {
    
    
            // 模拟耗时任务
            Thread.sleep(1000);
            return 42;
        });

        Futures.addCallback(future, new FutureCallback<Integer>() {
    
    
            @Override
            public void onSuccess(Integer result) {
    
    
                System.out.println("Task completed. Result: " + result);
            }

            @Override
            public void onFailure(Throwable throwable) {
    
    
                System.out.println("Task failed: " + throwable.getMessage());
            }
        }, executorService);

        executorService.shutdown();
    }
}

작업 결과:

Task 3 completed successfully.
Task 1 completed successfully.
Task 2 completed successfully.
Task 0 completed successfully.
Task 4 completed successfully.

위의 예에서는 먼저 ListeningExecutorService를 만든 다음 여기에 몇 가지 비동기 작업을 제출했습니다. 다음으로 Futures.addCallback() 메서드를 사용하여 ListenableFutures 목록을 반복하여 각 ListenableFuture에 리스너를 추가합니다.

리스너는 작업이 성공적으로 완료되고 실패할 때 각각 트리거되는 onSuccess() 및 onFailure() 메서드를 포함하여 FutureCallback 인터페이스를 구현합니다.

마지막으로 스레드 풀을 닫기 전에 Futures.allAsList()를 사용하여 모든 작업이 완료될 때까지 기다립니다.

Guava의 ListenableFuture는 비동기 작업 모니터링을 보다 편리하게 처리할 수 있지만 Guava 라이브러리를 도입해야 합니다. 예외를 적절하게 처리하고 스레드 풀을 종료하는 것이 매우 중요합니다.

요약하다:

동시 프로그래밍에는 일괄 작업의 동기화 및 관리를 달성하는 방법이 많이 있습니다.
다음은 몇 가지 일반적인 사항과 차이점입니다.

1、ExecutorCompletionService:

  1. ExecutorCompletionService를 사용하여 작업 배치를 제출하고 작업이 완료되면 완료된 순서대로 결과를 처리합니다.
  2. 작업이 완료된 순서대로 결과를 처리해야 하는 시나리오에 적합합니다.
  3. 모든 작업이 완료될 때까지 기다리지 않고 작업이 완료되는 즉시 결과를 처리할 수 있습니다.
  4. 대기열에서 완료된 작업을 가져오려면 take() 메서드를 사용하고, 비차단 가져오기에는 poll() 메서드를 사용하세요.

2、완성 가능한 미래:

  1. CompletableFuture를 사용하면 일괄 작업을 비동기적으로 제출하고 완료 시 일부 작업을 수행할 수 있습니다.
  2. 체인 작업, 결합 작업, 예외 처리 등 보다 유연한 작업을 제공합니다.
  3. thenApply(), thenAccept(), thenRun(), thenCompose() 등의 메소드를 통해 연속적으로 동작할 수 있습니다.
  4. allOf()와 같은 메서드를 사용하여 모든 작업이 완료될 때까지 기다리거나 anyOf()와 같은 메서드를 사용하여 작업이 완료될 때까지 기다릴 수 있습니다.
  5. 콜백을 추가하거나 whenComplete(), handler(),Exceptionly()등의 메소드를 사용하여 작업 결과 및 예외를 처리할 수 있습니다.

3、ListenableFuture:

  1. ListenableFuture(예: Guava의 ListenableFuture 또는 Spring의
    ListenableFuture)를 사용하면 작업 완료 시 작업을 처리하는 리스너를 추가할 수 있습니다.
  2. 작업 결과 및 예외를 처리하는 콜백과 유사한 방법을 제공합니다. addCallback() 메서드를 사용하여 콜백을 추가하면 성공 사례와 실패 사례를 별도로 처리할 수 있습니다.

4、invokeAll() 과 호출Any():

  1. InvokeAll() 메서드를 사용하여 작업 세트를 제출하고 모든 작업이 완료될 때까지 기다립니다.
  2. InvokeAny() 메서드를 사용하여 일련의 작업을 제출하고 그 중 하나가 완료될 때까지 기다립니다.
  3. 호출All()은 Future 객체가 포함된 목록을 반환하며, 작업의 결과를 하나씩 얻을 수 있습니다.
  4. 호출Any()는 성공적으로 완료된 첫 번째 작업의 결과를 반환합니다.

5. 필요에 따라 선택하세요

  1. 완료 순서대로 결과를 처리해야 하는 경우 ExecutorCompletionService를 사용할 수 있습니다.
  2. 보다 유연한 운영과 체인 처리가 필요한 경우 CompletableFuture를 선택할 수 있습니다.
  3. 이미 Guava 또는 Spring을 사용하고 있다면 ListenableFuture가 좋은 선택일 수 있습니다.
  4. 반면, InvokeAll() 및 InvokeAny()는 한 번에 여러 작업을 제출하고 결과를 기다리는 시나리오에 적합합니다.

Guess you like

Origin blog.csdn.net/FLGBgo/article/details/132168452