Java线程池
Java里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是ExecutorService。
ThreadPoolExecutor
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
相关参数
- corePoolSize:线程池中线程的数量
- maximumPoolSize:线程池中最大的线程数量
- keepAliveTime:当线程池数量超过corePoolSize值时,多余的空闲线程等待新任务的最长时间
- unit:keepAliveTime单位
- workQueue:执行前用于保持任务的队列
- threadFactory:线程工厂,用于创建线程
- handler:拒绝策略
handler(拒绝策略)分类
作用:当线程池中线程已经耗尽且等待队列也已经排满,则对于新任务采取合理的拒绝处理。
- AbortPolicy:该策略直接抛出异常(默认拒绝策略)
- CallerRunsPolicy:该策略线程直接调用运行该任务的execute本身。
- DiscardOldestPolicy:该策略直接丢弃最老的一个请求任务。
- DiscardPolicy:该策略直接丢弃无法处理的任务,不做任何处理(与AbortPolicy类似,但不抛出异常)
线程池类型
- newCachedThreadPool:创建一个可缓存的线程池。线程池大小依赖于JVM能够创建的最大线程大小。调用 execute 将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有60秒钟未被使用的线程。
- newSingleThreadExecutor:创建一个单线程的线程池,当该唯一线程因为异常退出会重新创建一个新线程。能够按照任务提交的顺序有序执行。
- newFixedThreadPool:创建一个固定大小的线程池,并以无界队列方式来运行这些线程。
- newScheduledThreadPool:创建一个大小无限,它可安排在给定延迟后运行命令或者定期地执行。
可能在日常开发中我们习惯性的创建一个线程池方法:
ExecutorService pool = Executors.newCachedThreadPool();
但是在阿里巴巴开发规范守约中提示:
线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式, 这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
说明: Executors各个方法的弊端:
- newFixedThreadPooL和newSingleThreadExecutor:主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至OOM。
- newCachedThreadPooL和newScheduledThreadPool:主要问题是线程数最大数是Integer.MAX_VALUE, 可能会创建数量非常多的线程,甚至OOM。
任务队列
- SynchronousQueue:不存储数据、可用于传递数据。每一个put操作必须等待一个take操作,否则不能继续添加元素。
- LinkedBlockingQueue:基于链表的阻塞队列。能够高效的处理并发数据,对于生产者和消费者端分别采用独立的锁来控制数据同步,以此提高整个队列的并发性能。
- ArrayBlockingQueue:用数组实现的有界阻塞队列。默认情况下不保证访问者公平的访问队列。