java并发工具包-CompletionService和ExecutorService痛点

1. 痛点阐述

  1. 来源:因为使用ExecutorService返回的Future,使用Future去获取任务的返回值;会导致阻塞;因此,引入CompletionService来任务执行完成一个,则返回一个结果,不会因为其中的一个任务没结束,而导致获取结果的阻塞。
  2. 注意点:使用的时候,需要明白各个技术点的注意点是什么,CompletionService只是为了解决Future的get数据阻塞问题;而ExecutorService关注的则是任务的完成。
  3. 痛点:
    1. 场景1:当执行批量任务的时候,规定了多长时间去执行任务,若任务未执行完成,则返回未执行完的任务;但是,使用executorService.shutdownNow();去打断在执行的任务;从而关闭线程池;但是,executorService.shutdownNow().返回的List<Runnabole>只是未执行的任务,并不包含被打断的任务;因此,造成了任务丢失了一个,且返回的任务,是无法获取到我们定义的任务的数据

2. 解决方案.

  1. 重现问题:
private static void trap1() throws InterruptedException {
    final ExecutorService executorService = Executors.newSingleThreadExecutor();
    List<Runnable> runnableList = IntStream.range(0, 5).boxed()
		    .map(CompletionServiceDemo::task)
		    .collect(Collectors.toList());

    ExecutorCompletionService<Object> completionService = new ExecutorCompletionService<>(executorService);
    runnableList.stream().forEach(r -> completionService.submit(Executors.callable(r)));

    TimeUnit.SECONDS.sleep(12);
    List<Runnable> noExecTask = executorService.shutdownNow(); // 获取的只是未执行的任务, 丢失了被打断的任务.
    System.out.println("Task Size: " + noExecTask.size());
    /**
     * 执行结果: 
     * Task[0] is starting...
     * Task[0] is end...
     * Task[1] is starting...
     * Task[1] is interrupt...
     * Task Size: 3
     */
}

private static Runnable task(int i) {
    return () -> {
        try {
            System.out.printf("Task[%d] is starting... \n", i);
            TimeUnit.SECONDS.sleep(i * 5 + 10);
            System.out.printf("Task[%d] is end... \n", i);
        } catch (InterruptedException e) {           
         	System.out.printf("Task[%d] is interrupt... \n", i);
        }    
    };
}
  1. 解决问题
private static void killTrap1() throws InterruptedException {
    final ExecutorService executorService = Executors.newSingleThreadExecutor();
    List<Callable> tasks = IntStream.range(0, 5).boxed().map(MyTask::new).collect(Collectors.toList());

    ExecutorCompletionService<Integer> completionService = new ExecutorCompletionService<>(executorService);
    tasks.forEach(completionService::submit);

    TimeUnit.SECONDS.sleep(12);
    executorService.shutdownNow();
    // 因为从ExecutorService中返回的不包含被interrupt的任务, 以及, 无法获取到传入的任务数据.
    // 因此使用刚刚自定义的MyTask, 因为这些任务时我们自己维护的,只是使用了线程池和completionService的机制。

    tasks.stream().filter(callable -> !((MyTask) callable).isSuccess()).forEach(System.out::println);
    /**
     * 任务执行结果:
     * Task[0] is starting...
     * Task[0] is end...
     * Task[1] is starting...
     * [ MyTask value = 1, success = false ]
     * [ MyTask value = 2, success = false ]
     * [ MyTask value = 3, success = false ]
     * [ MyTask value = 4, success = false ]
     */
}


private static class MyTask implements Callable<Integer> {

    private final Integer value;

    private boolean success = false;

    MyTask(Integer value) {        
    	this.value = value;
    }

    @Override
    public Integer call() throws Exception {        
    	System.out.printf("Task[%d] is starting... \n", value);
        TimeUnit.SECONDS.sleep(value * 5 + 10);
        System.out.printf("Task[%d] is end... \n", value);
        success = true;
        return value;
    }
    
    public boolean isSuccess() {
        return success;
    }
    
    @Override
    public String toString() {        
    	return "[ MyTask value = " + value + ", success = " + success + " ]";
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值