【并发编程】- 线程池使用ExecutorCompletionService解决Future的阻塞缺点

267 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第15天,点击查看活动详情

使用CompletionService解决Future的缺点

接口Future具有阻塞同步性,这种情况代码运行效率很低,可以使用CompletionService接口解决Future同步阻塞的缺点。

使用CompletionService接口中的take()方法,它的主要作用是取得Future对象,方法声明结构如下:

public Future take() throws InterruptedException

线程执行代码如下:

@Slf4j
public class FutureCallable implements Callable<String> {

    private String username;
    private Long sleepTime;

    public FutureCallable(String username,Long sleepTime){
        super();
        this.username=username;
        this.sleepTime=sleepTime;
    }

    @Override
    public String call() throws Exception {
        log.info("用户名:{}",username);
        Thread.sleep(sleepTime);
        return username;
    }
}

运行类执行代码如下:

@Slf4j
public class FutureRun {
    public static void main(String[] args) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("HH:mm:ss");
        FutureCallable g1 = new FutureCallable("G1", 5000L);
        FutureCallable g2 = new FutureCallable("G2", 4000L);
        FutureCallable g3 = new FutureCallable("G3", 3000L);
        FutureCallable g4 = new FutureCallable("G4", 2000L);
        FutureCallable g5 = new FutureCallable("G5", 1000L);

        ArrayList<Callable> callableArrayList = new ArrayList<>();
        callableArrayList.add(g1);
        callableArrayList.add(g2);
        callableArrayList.add(g3);
        callableArrayList.add(g4);
        callableArrayList.add(g5);

        ArrayList<Future> futureArrayList = new ArrayList<>();
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 10, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>());
        //使用CompletionService
        ExecutorCompletionService executorCompletionService = new ExecutorCompletionService(threadPoolExecutor);
        for (int i = 0; i < 5 ; i++) {
            executorCompletionService.submit(callableArrayList.get(i));
        }
        for (int i = 0; i < 5 ; i++) {
            try {
                log.info("等待输出第{}个返回值:{}",i+1,executorCompletionService.take().get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }

    }
}

运行结果如下:

[pool-1-thread-2] INFO com.ozx.concurrentprogram.executor.service.FutureCallable - 用户名:G2

[pool-1-thread-1] INFO com.ozx.concurrentprogram.executor.service.FutureCallable - 用户名:G1

[pool-1-thread-4] INFO com.ozx.concurrentprogram.executor.service.FutureCallable - 用户名:G4

[pool-1-thread-3] INFO com.ozx.concurrentprogram.executor.service.FutureCallable - 用户名:G3

[pool-1-thread-5] INFO com.ozx.concurrentprogram.executor.service.FutureCallable - 用户名:G5

[main] INFO com.ozx.concurrentprogram.executor.controller.FutureRun - 等待输出第1个返回值:G5

[main] INFO com.ozx.concurrentprogram.executor.controller.FutureRun - 等待输出第2个返回值:G4

[main] INFO com.ozx.concurrentprogram.executor.controller.FutureRun - 等待输出第3个返回值:G3

[main] INFO com.ozx.concurrentprogram.executor.controller.FutureRun - 等待输出第4个返回值:G2

[main] INFO com.ozx.concurrentprogram.executor.controller.FutureRun - 等待输出第5个返回值:G1

从运行结果看出CompletionService完全解决了Future阻塞的特性,简单就是使用CompletionService接口后,哪个任务先执行完,哪个任务的返回值就先输出。但是在CompletionService接口中如果当前没有任务被执行完,那么CompletionService.take().get()也是会阻塞特性。