美文网首页Java 杂谈程序员
Java线程池执行任务的两种机制,invokeAll和 Comp

Java线程池执行任务的两种机制,invokeAll和 Comp

作者: 大侠陈 | 来源:发表于2019-01-16 20:11 被阅读8次

通常Java线程池执行的任务有两种类型,一种是不带返回值的Runnable, 另一种是带返回值的Callable。

对于不带返回值的任务通常我们不太关注任务是否执行结束以及结束后应该做做些什么,我们将任务提交给线程池, 然后顾自己干别的事情。

带返回值的任务执行结果通常受到当前任务的依赖,任务提交给线程池后还需要等待任务的返回。对于任务结果我们会有不同的需求,有时候当前任务依赖所有提交给线程池的任务的结果, 而有时候有只依赖某一个任务的执行结果,就好比饭店的服务员需要等待宝箱中所有顾客用餐完毕才来收拾,而食堂的阿姨却可以单个学生用餐完毕而来收拾。

Java线程池对对于这两种需求提供不同的解决方案

对于依赖所有任务执行结果的可以直接使用线程池的invokeAll方法

public class Main {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        List<Callable<Integer>> tasks = new ArrayList<>();
        for( int i = 0; i < 10; i++) {
            tasks.add(()->{
                Random random = new Random();
                int second = random.nextInt(10);
                Thread.sleep(second * 1000) ;
                return second;
            });
        }
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        List<Future<Integer>> futures = executorService.invokeAll(tasks);
        for( int i = 0; i < futures.size(); i++) {
            System.out.println(futures.get(i).get());
        }

        executorService.shutdown();
    }
}

以上程序清单中的线程池执行10个任务,这些任务会做随机延时,所有的任务都放在tasks变量中。

我们初始化一个长度为时的固定大小的线程池执行这些任务,方法invokeAll调用会阻塞,在所有任务执行完毕后返回,然后程序打印这些返回结果。我们运行这段代码会卡断很长时间,接着瞬间出结果, 这是invokeAll的特性:所欲任务必须执行完毕后才返回。

对于不依赖所有任务的执行结果,而可以单独处理每个任务结果的,invokeAll就显得不友好了,虽然最终结果没区别,执行完所有任务都需要话同样的时间,可是执行完一个任务就处理一个任务的结果不是显得更加人性化么,比如加载多张网络图片,加载完成一张就显示一张显然有更好的用户体验,对于这种需求我们可以使用CompletionService。

CompletionService能逐个返回任务的执行结果,谁先执行完毕返回谁。 它利用了阻塞队列的特想,当它察觉到有任务执行完毕时则将执行的结果,一个Future放入它维护的一个无界阻塞队列,外部程序就可以通过take方法拿取,如果阻塞队列为空,也就是还没有执行完毕的任务, 那么take方法则阻塞,外部程序继续等待。

public class Main {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        List<Callable<Integer>> tasks = new ArrayList<>();
        for( int i = 0; i < 10; i++) {
            tasks.add(()->{
                Random random = new Random();
                int second = random.nextInt(10);
                Thread.sleep(second * 1000) ;
                return second;
            });
        }
        ExecutorService executorService = Executors.newFixedThreadPool(10);

        CompletionService<Integer> completionService = new ExecutorCompletionService(executorService);
        tasks.forEach(task -> completionService.submit(task));

        for( int i = 0; i < tasks.size(); i++) {
            System.out.println(completionService.take().get());
        }

        executorService.shutdown();
    }
}

执行上面的代码不会长时间卡断后瞬间出结果,它会平缓的打印每个任务的执行结果, 直到所有任务执行完毕而结束程序。

相关文章

  • Java线程池执行任务的两种机制,invokeAll和 Comp

    通常Java线程池执行的任务有两种类型,一种是不带返回值的Runnable, 另一种是带返回值的Callable。...

  • 线程池核心知识点

    —读《java并发编程》笔记 为什么需要线程执行框架 不用线程池是否也有异步执行任务的方案? 有两种方案,一是单独...

  • java并发编程锁机制

    之前系列文章都在叙述java线程池的设计以及实现机制,没有涉及java并发编程的锁机制,这是因为锁机制与线程池是...

  • 线程池工作机制与原理

    书接上文, Java线程池 。接下来记录一下线程池的工作机制和原理 线程池的两个核心队列: 线程等待池,即线程队列...

  • 并发编程09--Java中的线程池

    Java中的线程池 Java中的线程池是运用场景最多的并发框架,几乎所有需要异步或并发执行任务的程序都可以使用线程...

  • 04 线程池原理与AsyncTask

    1 什么是线程池?为什么要用线程池? Java中的线程池是运用场景最多的并发框架,几乎所有需要异步或并发执行任务的...

  • 线程池原理与AsyncTask

    什么是线程池?为什么要用线程池?Java中的线程池是运用场景最多的并发框架,几乎所有需要异步或并发执行任务的程序都...

  • 线程池

    线程池的好处 Java 中的线程池是运用场景最多的并发框架,几乎所有需要异步或并发执行任务的程序都可以使用线程池。...

  • 多线程(五)——线程池原理剖析&锁的深度化

    什么是线程池 Java中的线程池是运用场景最多的并发框架,几乎所有需要异步或并发执行任务的程序都可以使用线程池。在...

  • 5.线程池原理剖析&锁的深度化

    线程池 什么是线程池 Java中的线程池是运用场景最多的并发框架,几乎所有需要异步或并发执行任务的程序都可以使用线...

网友评论

    本文标题:Java线程池执行任务的两种机制,invokeAll和 Comp

    本文链接:https://www.haomeiwen.com/subject/tprvdqtx.html