JAVA并发编程之JAVA--ThreadPoolExecutor详解

Java 线程池的顶级接口都是Executor,但是严格意义上Executor 并不是一个线程池,而只是一个执行线程的工具,真正的线程池接口是ExecutorService,ThreadPoolExecutor 是ExecutorService 的默认实现,由ThreadPoolExecutor 类生成对应的线程池。

ThreadPoolExecutor 是Executors类的底层实现 其完整构造方法:

ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)

参数解释:
corePoolSize :池中所保存的线程数,包括空闲线程
maximumPoolSize : 池中最大的线程数
keepAliveTime :当池中的线程是多于corePoolSize 数,此为终止多余的线程在等待处理任务的最长时间
Unit : 参数的执行时间单位
workQueue :执行前用于保持任务的队列。此队列仅保持由execute 提交的Runnable 任务。
threadFactory :执行程序创建线程时所用的工厂
Handler :用于超出线程范围和队列容量而执行被阻塞时所用的处理程序

workQueue 的 3中类型:
直接提交:工作队列的默认选项是 SynchronousQueue; 改队列在某次添加进入元素后在元素在取走之前无法在添加元素;改队列相当于一个中间传递者,负责把生产者得数据传递给消费者;队列中并不缓存元素,不允许遍历整个队列,也不允许查看队列是否有元素;可以理解为:生产者线程产生数据等待消费线程消费数据,然后生产者消费者握手后一起离开;

无界队列:使用无界队列(如不具有预定义容量得 默认队列数 2的 32 次方 -1;LinkedBlockingQueue),当线程池 中所有的corePoolSize 均繁忙时,新任务都进入队列中,这样线程池中线程不会超过corePoolSize 数量,此时maximumPoolSize 失效。当每个任务独立于其他任务,适用于无界队列;改无界队列,可以用于处理瞬态突发请求,当程序无法及时处理任务时,可以使用无界队列进行任务的存放;

有界队列:当使用有限的maximumPoolSize,有界队列(如ArrayBlockingQueue),有助于防止资源耗尽。但是队列和最大线线程数需要进行折衷:大队列小型池,有助于降低cpu 使用率,降低系统资源和上下文切换开销,但是可能导致人工降低吞吐量(如果任务频繁阻塞,则线程执行任务的时间可能超出预期时间)。小队列大型池,cpu 使用率较高,但是会升高系统资源和上下文切换开销,也会降低吞吐量;

举例一:使用直接提交策略即SynchronousQueue
java 线程池的 创建按需的线程池newCachedThreadPool 队列的实现
new ThreadPoolExecutor(2, 3,
60L, TimeUnit.SECONDS,
new SynchronousQueue(),
new RecorderThreadFactory(“CookieRecorderPool”),
new ThreadPoolExecutor.CallerRunsPolicy());

说明:当核心线程已经有两个在运行
1 此时进入一个新的任务A,由于当前的核心线程大于或等于corePoolSize
, Executor 始终首选将新任务添加到队列中而不创建新的线程,所以A 被添加到SynchronousQueue 中;
2 又进入一个新的任务B ,且两个核心线程仍然在忙,此时往SynchronousQueue
加入任务失败 ,当前线程数少于maximumPoolSize 则创建新的线程 来运行整个任务B;
3 然后又进入一个新的任务C,且3个线程都在忙碌,此时无法加入队列,也无法创建新的线程,所以只好执行异常策略;

所以使用SynchronousQueue 通常要求maximumPoolSize 是无界的;

举例二:使用无界 队列,如:不具有预定义容量的LinkedBlockingQueue
java 线程池的创建固定线程的线程池newFixedThreadPool()
new ThreadPoolExecutor(2, 2,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());

说明:当核心线程有一个正在运行
1 此时进入一个线程,如果运行的线程少有核心线程,直接添加新的线程,而不进行排队;
2 又进入一个线程B ,且2个核心线程均在忙碌,当前运行的线程大于或者等于核心线程,则Executor 直接将任务添加到队列中,所有的线程都在忙碌,当队列无法在添加新的任务到队列中,当当前的线程已经达到maximumPoolSize 数量,任务将被拒绝;

举例三:使用有界队列 ArrayBlockingQueue
与直接提交和无界队列,有界队列可优先避免服务器的资源耗尽
new ThreadPoolExecutor(
2, 4, 30, TimeUnit.SECONDS,
new ArrayBlockingQueue(2),
new RecorderThreadFactory(“CookieRecorderPool”),
new ThreadPoolExecutor.CallerRunsPolicy());

说明: 目前当线程有两个核心线程在运行
1 新进入两个任务A,B ,且两个核心线程正在忙碌, 此时线程数大于或等于核心线程,则线程A和线程B 进入 有界队列中;
2 又进入两个任务C ,D ,则增加线程运行 C,D 任务;
3 此时在次进入一个新的任务,队列此时无法添加任务,线程数也已达到最大线程数,则执行拒绝策略;

RejectedExecutionHandler :超出线程范围和队列容量 的异常处理机制
Executor 已经默认包含了4中策略:
策略1:CallerRunsPolicy:线程调用运行该任务的excute本身,此策略用来减缓任务的提交速度
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
此种方式不进行任务的丢弃
策略2:AbortPolicy 丢弃任务并抛出 RejectedExecutionException
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException();
}
这种策略直接丢弃任务并抛出异常
策略3 :DiscardPolicy 丢弃任务但不抛出异常:
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
策略4:DiscardOldestPolicy 如果执行程序尚未关闭,则位于队列头部的任务将被删除,然后重试执行程序(如果在次失败,则重复此过程)
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute®;
}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值