用例表示不同类型的并发结构

  1. ExecutorService 的

    ExecutorService executor = Executors.newFixedThreadPool(50);

    它简单易用。它隐藏了 ThreadPoolExecutor 的低级细节。

    Callable/Runnable 任务的数量很少并且在无界队列中堆积任务不会增加内存并降低系统性能时,我更喜欢这个。如果你有 CPU/Memory 约束,我更喜欢使用带有容量限制的 ThreadPoolExecutorRejectedExecutionHandler 来处理任务的拒绝。

  2. CountDownLatch

    CountDownLatch 将使用给定的计数进行初始化。通过调用 countDown() 方法减少此计数。等待此计数达到零的线程可以调用其中一个 await() 方法。调用 await() 会阻塞线程,直到计数达到零。此类使 java 线程等待,直到其他线程集完成其任务。

    用例:

    1. 实现最大并行性:有时我们希望同时启动多个线程以实现最大并行度

    2. 在开始执行之前等待 N 个线程完成

    3. 死锁检测。

  3. ThreadPoolExecutor :它提供更多控制。如果应用程序受到待处理的 Runnable / Callable 任务数量的限制,则可以通过设置最大容量来使用有界队列。队列达到最大容量后,你可以定义 RejectionHandler。Java 提供了四种类型的 RejectedExecutionHandler 策略

    1. ThreadPoolExecutor.AbortPolicy,处理程序在拒绝时抛出运行时 RejectedExecutionException。

    2. ThreadPoolExecutor.CallerRunsPolicy`,调用 execute 本身的线程运行任务。这提供了一种简单的反馈控制机制,可以降低新任务的提交速度。

    3. ThreadPoolExecutor.DiscardPolicy 中,简单地删除了无法执行的任务。

    4. ThreadPoolExecutor.DiscardOldestPolicy,如果执行程序未关闭,则删除工作队列头部的任务,然后重试执行(可能会再次失败,导致重复执行)。

如果要模拟 CountDownLatch 行为,可以使用 invokeAll() 方法。

  1. 你没有引用的一种机制是 ForkJoinPool

    在 Java 7 中将 ForkJoinPool 添加到 Java 中 .ForkJoinPool 类似于 Java ExecutorService,但有一点不同。ForkJoinPool 使任务很容易将他们的工作分成更小的任务,然后提交给 ForkJoinPool。当自由工作线程从繁忙的工作线程队列中窃取任务时,任务窃取发生在 ForkJoinPool 中。

    Java 8 在 ExecutorService 中引入了另外一个 API 来创建工作窃取池。你不必创建 RecursiveTaskRecursiveAction,但仍然可以使用 ForkJoinPool

    public static ExecutorService newWorkStealingPool()
    

    使用所有可用处理器作为其目标并行级别创建工作窃取线程池。

    默认情况下,它会将 CPU 核心数作为参数。

所有这四种机制互为补充。根据你要控制的粒度级别,你必须选择正确的粒度级别。