创建=>销毁=>创建
对系统对开销很大,使用线程池可以避免重复的开销一说到线程池自然就会想到池化技术。
其实所谓池化技术,就是把一些能够复用的东西放到池中,避免重复创建、销毁的开销,从而极大提高性能。
常见池化技术的例如:
JDK 1.5 推出了三大API用来创建线程:
Executors.newCachedThreadPool()
:无限线程池(最大21亿)Executors.newFixedThreadPool(nThreads)
:固定大小的线程池Executors.newSingleThreadExecutor()
:单个线程的线程池这三个API的底层其实都是由同一个类实现的:ThreadPoolExecutor
类
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
ThreadPoolExecutor
类ThreadPoolExecutor
类主要有以下七个参数:
int corePoolSize
: 核心线程池大小int maximumPoolSize
: 最大核心线程池大小long keepAliveTime
: 线程空闲后的存活时间TimeUnit unit
: 超时单位BlockingQueue<Runnable> workQueue
: 阻塞队列ThreadFactory threadFactory
: 线程工厂:创建线程的,一般默认RejectedExecutionHandler handle
: 拒绝策略拒绝策略就是当队列满时,线程如何去处理新来的任务。
Java内置了四种拒绝策略:
ThreadPoolExecutor.CallerRunsPolicy()
ThreadPoolExecutor.AbortPolicy()
ThreadPoolExecutor.DiscardPolicy()
ThreadPoolExecutor.DiscardOldestPolicy()
功能:只要线程池没有关闭,就由提交任务的当前线程处理。
使用场景:一般在不允许失败、对性能要求不高、并发量较小的场景下使用。
功能:当触发拒绝策略时,直接抛出拒绝执行的异常
使用场景:ThreadPoolExecutor
中默认的策略就是AbortPolicy
,由于ExecutorService
接口的系列ThreadPoolExecutor
都没有显示的设置拒绝策略,所以默认的都是这个。
功能:直接丢弃这个任务,不触发任何动作
使用场景:提交的任务无关紧要,一般用的少。
功能:弹出队列头部的元素,然后尝试执行,相当于排队的时候把第一个人打死,然后自己代替
使用场景:发布消息、修改消息类似场景。当老消息还未执行,此时新的消息又来了,这时未执行的消息的版本比现在提交的消息版本要低就可以被丢弃了。
RUNNING
自然是运行状态,指可以接受任务执行队列里的任务SHUTDOWN
指调用了 shutdown()
方法,不再接受新任务了,但是队列里的任务得执行完毕。STOP
指调用了 shutdownNow()
方法,不再接受新任务,同时抛弃阻塞队列里的所有任务并中断所有正在执行任务。TIDYING
所有任务都执行完毕,在调用 shutdown()/shutdownNow()
中都会尝试更新为这个状态。TERMINATED
终止状态,当执行 terminated()
后会更新为这个状态。提交一个任务到线程池中,线程池的处理流程如下:
判断线程池里的核心线程是否都在执行任务
线程池判断工作队列是否已满
判断线程池里的所有线程是否都处于工作状态
不过最好不要使用Executors
来创建线程,原因如下(参考自——阿里巴巴Java开发手册):
FixedThreadPool
和 SingleThreadPool
: 允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOMCachedThreadPool
: 允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM推荐使用ThreadPoolExecutor
类自行创建
// 自定义线程池
ExecutorService threadPool = new ThreadPoolExecutor(
2,
Runtime.getRuntime().availableProcessors(),//CPU的核心数,适合CPU密集型任务
3,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.DiscardOldestPolicy());
线程池不是越大越好,要根据任务类型合理进行配置
有两个方法可以执行任务execute
和submit
execute
提交没有返回值,不能判断是否执行成功。submit
会返回一个Future
对象,通过Future
的get()
方法来获取返回值。区别:
execute
提交的方式只能提交一个Runnable的对象submit
有三种submit
有返回值,而execute
没有submit
方便Exception
处理execute
是Executor
接口中唯一定义的方法submit
是ExecutorService
(该接口继承Executor
)中定义的方法线程池使用完毕,需要对其进行关闭,有两种方法
shutdown()
:不再继续接收新的任务,执行完成已有任务后关闭
shutdownNow()
:直接关闭,若果有任务尝试停止