Java基础-Thread-线程池ThreadPoolExecutor与ExecutorService

前言

我理解的 ExecutorService只是对ThreadPoolExecutor做了一层封装,但是ThreadPoolExecutor支持更多BlockingQueue且更为灵活.
所以我一般都是使用ThreadPoolExecutor.

可以通过看源码来说明 :
以 ExecutorService cachedThreadPool = Executors.newCachedThreadPool() 为例
newCachedThreadPool() 源码 :

public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
    
Executors.newCachedThreadPool() 相当于 
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
 ExecutorService 包括 : 
 - newFixedThreadPool : 使用LinkedBlockingQueue
 - newCachedThreadPool : 使用SynchronousQueue
 - newScheduledThreadPool : 使用DelayedWorkQueue
 - newSingleThreadExecutor : 使用LinkedBlockingQueue
BlockingQueue 类别及说明 : 
1> 数组阻塞队列ArrayBlockingQueue : 一个由数组支持的有界阻塞队列。此队列按 FIFO(先进先出)原则对元素进行排序。队列的头部 是在队列中存在时间最长的元素。队列的尾部 是在队列中存在时间最短的元素。新元素插入到队列的尾部,队列获取操作则是从队列头部开始获得元素。
2> 延迟队列DelayQueue : Delayed 元素的一个无界阻塞队列,只有在延迟期满时才能从中提取元素。该队列的头部 是延迟期满后保存时间最长的 Delayed 元素。如果延迟都还没有期满,则队列没有头部,并且 poll 将返回 null。当一个元素的 getDelay(TimeUnit.NANOSECONDS) 方法返回一个小于等于 0 的值时,将发生到期。即使无法使用 take 或 poll 移除未到期的元素,也不会将这些元素作为正常元素对待。例如,size 方法同时返回到期和未到期元素的计数。此队列不允许使用 null 元素
3> 链阻塞队列 LinkedBlockingQueue : LinkedBlockingQueue 类实现了 BlockingQueue 接口。LinkedBlockingQueue 内部以一个链式结构(链接节点)对其元素进行存储。如果需要的话,这一链式结构可以选择一个上限。如果没有定义上限,将使用 Integer.MAX_VALUE 作为上限。LinkedBlockingQueue 内部以 FIFO(先进先出)的顺序对元素进行存储。队列中的头元素在所有元素之中是放入时间最久的那个,而尾元素则是最短的那个。
4> 具有优先级的阻塞队列 PriorityBlockingQueue : PriorityBlockingQueue 类实现了 BlockingQueue 接口。一个无界阻塞队列,它使用与类 PriorityQueue 相同的顺序规则,并且提供了阻塞获取操作。虽然此队列逻辑上是无界的,但是资源被耗尽时试图执行 add 操作也将失败(导致OutOfMemoryError)。此类不允许使用 null 元素。依赖自然顺序的优先级队列也不允许插入不可比较的对象(这样做会导致抛出 ClassCastException)。此类及其迭代器可以实现 Collection 和 Iterator 接口的所有可选 方法。iterator() 方法中提供的迭代器并不 保证以特定的顺序遍历 PriorityBlockingQueue 的元素。如果需要有序地进行遍历,则应考虑使用 Arrays.sort(pq.toArray())。此外,可以使用方法 drainTo 按优先级顺序移除 全部或部分元素,并将它们放在另一个 collection 中。在此类上进行的操作不保证具有同等优先级的元素的顺序。如果需要实施某一排序,那么可以定义自定义类或者比较器,比较器可使用修改键断开主优先级值之间的联系。例如,以下是应用先进先出 (first-in-first-out) 规则断开可比较元素之间联系的一个类。要使用该类,则需要插入一个新的 FIFOEntry(anEntry) 来替换普通的条目对象。
5> 同步队列 SynchronousQueue : SynchronousQueue 类实现了 BlockingQueue 接口。SynchronousQueue 是一个特殊的队列,它的内部同时只能够容纳单个元素。如果该队列已有一元素的话,试图向队列中插入一个新元素的线程将会阻塞,直到另一个线程将该元素从队列中抽走。同样,如果该队列为空,试图向队列中抽取一个元素的线程将会阻塞,直到另一个线程向队列中插入了一条新的元素。据此,把这个类称作一个队列显然是夸大其词了。它更多像是一个汇合点。
6> 阻塞双端队列 BlockingDeque : java.util.concurrent 包里的 BlockingDeque 接口表示一个线程安放入和提取实例的双端队列。本小节我将给你演示如何使用 BlockingDeque。BlockingDeque 类是一个双端队列,在不能够插入元素时,它将阻塞住试图插入元素的线程;在不能够抽取元素时,它将阻塞住试图抽取的线程。deque(双端队列) 是 "Double Ended Queue" 的缩写。因此,双端队列是一个你可以从任意一端插入或者抽取元素的队列。
7> 链阻塞双端队列 LinkedBlockingDeque : 一个基于已链接节点的、任选范围的阻塞双端队列。可选的容量范围构造方法参数是一种防止过度膨胀的方式。如果未指定容量,那么容量将等于 Integer.MAX_VALUE。只要插入元素不会使双端队列超出容量,每次插入后都将动态地创建链接节点。大多数操作都以固定时间运行(不计阻塞消耗的时间)。异常包括 remove、removeFirstOccurrence、removeLastOccurrence、contains、iterator.remove() 以及批量操作,它们均以线性时间运行。
  /**
	* 2 : corePoolSize
	* 5 : maximumPoolSize
	* 30 : keepAliveTime
	* LinkedBlockingQueue(10最大可阻塞数) : 使用哪种策略
	 * new ThreadPoolExecutor.AbortPolicy() : 异常策略[抛异常]
  */
  ThreadPoolExecutor executorService = new ThreadPoolExecutor(2, 5, 30, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(10), new ThreadPoolExecutor.AbortPolicy());

线程池的拒绝策略

线程池拒绝策略
 - ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。[默认] 
 - ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。 
 - ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,执行后面的任务
 - ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务 

线程池需要配置的参数


 - corePoolSize : 核心线程数,核心线程会一直存活,即使没有任务需要处理。当线程数小于核心线程数时,即使现有的线程空闲,线程池也会优先创建新线程来处理任务,而不是直接交给现有的线程处理。核心线程在allowCoreThreadTimeout被设置为true时会超时退出,默认情况下不会退出。
 - maxPoolSize : 当线程数大于或等于核心线程,且任务队列已满时,线程池会创建新的线程,直到线程数量达到maxPoolSize。如果线程数已等于maxPoolSize,且任务队列已满,则已超出线程池的处理能力,线程池会拒绝处理任务而抛出异常。
 - keepAliveTime : 当线程空闲时间达到keepAliveTime,该线程会退出,直到线程数量等于corePoolSize。如果allowCoreThreadTimeout设置为true,则所有线程均会退出直到线程数量为0。
 - allowCoreThreadTimeout : 是否允许核心线程空闲退出,默认值为false。
 - queueCapacity : 任务队列容量。从maxPoolSize的描述上可以看出,任务队列的容量会影响到线程的变化,因此任务队列的长度也需要恰当的设置。任务队列的长度要根据核心线程数,以及系统对任务响应时间的要求有关。队列长度可以设置为(corePoolSize/tasktime)*responsetime: (20/0.1)*2=400,即队列长度可设置为400。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Java线程池ThreadPoolExecutorJava提供的一个用于管理和复用线程的工具类。它可以帮助我们更有效地管理线程资源,提高程序的性能和可维护性。 下面是一个简单的使用ThreadPoolExecutor的示例代码: ```java import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadPoolExample { public static void main(String[] args) { // 创建一个线程池,其中包含5个线程 ExecutorService executor = Executors.newFixedThreadPool(5); // 提交任务给线程池执行 for (int i = 0; i < 10; i++) { Runnable worker = new WorkerThread("Task " + i); executor.execute(worker); } // 关闭线程池 executor.shutdown(); while (!executor.isTerminated()) { // 等待所有任务完成 } System.out.println("所有任务已完成"); } } class WorkerThread implements Runnable { private String taskName; public WorkerThread(String taskName) { this.taskName = taskName; } @Override public void run() { System.out.println(Thread.currentThread().getName() + " 开始执行任务:" + taskName); try { // 模拟任务执行时间 Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " 完成任务:" + taskName); } } ``` 上述代码中,首先通过`Executors.newFixedThreadPool(5)`创建了一个包含5个线程的线程池。然后使用`executor.execute(worker)`提交任务给线程池执行,其中`worker`是实现了`Runnable`接口的任务对象。任务会被线程池中的线程异步执行。 最后,通过`executor.shutdown()`关闭线程池,并使用`executor.isTerminated()`等待所有任务完成。完成后输出"所有任务已完成"。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小安灬

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值