简单说说Java线程池

1,835 阅读4分钟

1 Java线程池简介

Java1.5大师Doug Lea的作品。线程池能够对线程进行统一的分配,通过固定数量的线程来负责处理任务,避免了频繁的创建和销毁对象,使线程能够重复的利用,执行多个任务。

2 Java线程池的种类

线程工厂Executors实现的种类

种类 实现 基于的阻塞队列 说明
固定大小线程池 FixedThreadPool LinkedBlockingQueue 固定线程数量,任务队列无界
交付模式线程池 CachedThreadPool SynchronousQueue 线程数量不限制,任务队列不存放数据
并行线程池 WorkStealingPool FIFO_QUEUE或LIFO_QUEUE 线程数量不限制,分割任务执行
定时任务线程池 ScheduledThreadPool DelayedWorkQueue 线程数量不限制,任务按时间优先级执行
单例固定线程池 SingleThreadExecutor LinkedBlockingQueue 线程数量1个,任务按加入顺序执行

3 Java线程池的实现

3.1 阻塞队列

队列 实现 说明
LinkedBlockingQueue 基于链表和显示锁实现的队列 类似于LinkedList支持并发
SynchronousQueue 基于双栈/双队列和显示锁实现的交付队列 基于双栈/双队列算法
DelayedWorkQueue 基于优先级队列和显示锁实现的延迟队列 与优先级队列类似,由二叉堆算法实现

3.2 线程工厂Executors

线程池主体流程

3.2.1 CachedThreadPool

  • 重用之前的线程(线程池中未被销毁的线程)
  • 如果没有可用的线程,则创建一个新线程并添加到池中
  • 池中线程默认为60s未使用就被终止和移除
  • 长期闲置的池将会不消耗任何资源

执行execute方法时,首先会先执行SynchronousQueue的offer方法提交任务,并查询线程池中是否有空闲线程来执行SynchronousQueue的poll方法来移除任务。如果有,则配对成功,将任务交给这个空闲线程。否则,配对失败,创建新的线程去处理任务;当线程池中的线程空闲时,会执行SynchronousQueue的poll方法等待执行SynchronousQueue中新提交的任务。若超过60s依然没有任务提交到SynchronousQueue,这个空闲线程就会终止;因为maximumPoolSize是无界的,所以提交任务的速度 > 线程池中线程处理任务的速度就要不断创建新线程;每次提交任务,都会立即有线程去处理,因此CachedThreadPool适用于处理大量、耗时少的任务。

交付(缓存)线程池的优势与弊病

优点: 任务立即执行,线程自动回收

缺点: 瞬间太高并发情况下会出现瞬间创建成千上万个线程,导致系统瘫痪

使用场景

OKHTTP 框架

3.2.2 FixedThreadPool

  • 创建重用固定数量线程的线程池
  • 当所有线程都处于活动状态时,如果提交了其他任务, 他们将在队列中等待一个线程可用
  • 任务数量达到最大时,再加入会被拒绝执行
  • 线程会一直存在,直到调用shutdown

FixedThreadPool只有核心线程,且数量固定,没有非核心线程。keepAliveTime设置为0L,代表多余的线程会被立即终止。因为不会产生多余的线程,所以keepAliveTime是无效的参数;任务队列采用了无界的阻塞队列LinkedBlockingQueue(容量默认为Integer.MAX_VALUE)。

FixedThreadPool线程池的优势与弊病

优点: 线程固定数量相对可控

缺点: 高并发下的任务执行会变得缓慢,都会在队列中进行等待。

使用场景

Tomcat 工作线程

3.2.3 ScheduledThreadPool

  • 设定延迟时间,定期执行
  • 空闲线程会进行保留

当执行ScheduledThreadPoolExecutor的scheduleAtFixedRate或scheduleWithFixedDelay方法,会向DelayedWorkQueue添加一个实现RunnableScheduledFuture接口的任务包装类ScheduledFutureTask,并检查运行的线程是否达到核心线程数corePoolSize。如果没有就新建线程,并启动。但并非立即执行任务,而是去DelayedWorkQueue中取任务包装类ScheduledFutureTask,然后再去执行任务; 如果运行的线程达到了corePoolSize,就把任务添加到任务队列DelayedWorkQueue中;DelayedWorkQueue会将任务排序,先执行的任务放在队列的前面。 任务执行完后,ScheduledFutureTask中的变量time改为下次要执行的时间,并放回到DelayedWorkQueue中。

3.3 拒绝策略

策略 说明
AbortPolicy 当任务添加到线程池中被拒绝时,它将抛出RejectedExecutionException 异常。
CallerRunsPolicy 当任务添加到线程池中被拒绝时,会在线程池当前正在运行的Thread线程池中处理被拒绝的任务。
DiscardOldestPolicy 当任务添加到线程池中被拒绝时,线程池会放弃等待队列中最旧的未处理任务,然后将被拒绝的任务添加到等待队列中。
DiscardPolicy 当任务添加到线程池中被拒绝时,线程池将丢弃被拒绝的任务。

4 说明

参考资料

线程池之ScheduledThreadPool学习 blog.csdn.net/qq_36299025…

五种线程池的对比与使用

www.jianshu.com/p/135c89001…

其他资料

Java优先级队列DelayedWorkQueue原理分析 www.jianshu.com/p/587901245…

SynchronousQueue原理详解-公平模式 www.cnblogs.com/dwlsxj/p/Th…

SynchronousQueue原理详解-非公平模式 www.cnblogs.com/dwlsxj/p/sy…