线程池
使用Executors来创建线程池,失去了线程池的灵活性,而且存在隐患,可能导致资源耗尽。
但是:在开发中不允许使用Executors去创建线程池,而是通过ThreadPoolExecutor的方式,这样可以避免资源耗尽的风险。原因是:
FixedThreadPool和SingleThreadPool:允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM。
CachedThreadPool和ScheduledThreadPool:允许的创建线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM。
ThreadPoolExecutor的重要参数
corePoolSize:核心线程数量
maximumPoolSize:最大线程数量
workQueue:等待队列,当线程数量大于corePoolSize时,任务封装成worker放入队列。keepAliveTime:当线程数超过corePoolSize时,超过此时间空闲,则回收。
timeUnit:时间单位
threadFactory:指定创建的线程池类型
handler:拒绝策略
workQueue参数
ArrayBlockingQueue
LinkedBlockingQueue
SynchronousBlockingQueue
PriorityBlockingQueue
SynchronousQueue是一个不存储元素的阻塞队列。每一个put操作必须等待一个take操作,否则不能继续添加元素。
拒绝策略参数
AbortPolicy抛出RejectedExecutionException
DiscardPolicy什么也不做,直接忽略
DiscardOldestPolicy丢弃执行队列中最老的任务,尝试为当前提交的任务腾出位置
CallerRunsPolicy直接由提交任务者执行这个任务
过程原理
If fewer than corePoolSize threads are running, the Executor always prefers adding a new thread rather than queuing.
If corePoolSize or more threads are running, the Executor always prefers queuing a request rather than adding a new thread.
If a request cannot be queued, a new thread is created unless this would exceed maximumPoolSize, in which case, the task will be rejected.
即: corePoolSize -> 任务队列 -> maximumPoolSize -> 拒绝策略
ThreadPoolExecutor类型
借助ThreadPoolExecutor创建一些特定的线程池
ThreadPoolExecutor, ScheduledThreadPoolExecutor通常由工厂类Executors创建。
Executors可以创建三种类型的ThreadPoolExecutor:
SingleThreadPool,FixedThreadPool和CachedThreadPool。
Executors可以创建2种类型的ScheduledThreadPoolExecutor:
SingleScheduledThreadPoo和ScheduledThreadPool。
SingleThreadPool:适用于需要保证顺序地执行各个任务;并且在任意时间点,不会有多个线程是活动的应用场景。它填写的参数为corepoolsize为1,最大size也为1,使用LinkedBlockingQueue
FixedThreadPool:固定数量线程池(newFixedThreadPool),适用于为了满足资源管理的需求,而需要限制当前线程数量的应用场景,它适用于负载比较重的服务器。使用LinkedBlockingQueue
newCachedThreadPool:创建一个会根据需要创建新线程的,适用于执行很多的短期异步任务的小程序,或者是负载较轻的服务器。它填写的corePoolsize为0,使用的队列为SynchronousQueue。
线程池状态
running:能接受,能处理
shutdown:不接受新任务,能处理已添加
stop:不接受新任务,不处理已添加
tidying:由shutdown和stop转换而来
terminated:线程时彻底终止。由tidying执行terminated()转换而来
Executors快捷创建线程
newFixedThreadPool(int nThreads)创建固定大小的线程池
newSingleThreadExecutor()创建只有一个线程的线程池
newCachedThreadPool()创建一个不限线程数上限的线程池,任何提交的任务都将立即执行
使用ThreadPoolExecutor安全创建线程
填写ThreadPoolExecutor重要参数:
ExecutorService executorService = new ThreadPoolExecutor(2, 2,
0, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(512), // 使用有界队列,避免OOM
new ThreadPoolExecutor.DiscardPolicy());
提交任务
Future submit(Callable task):
关注返回值,用future.get()获得返回信息
Future submit(Runnable task) 不关注返回值
void execute(Runnable command) 不关注返回值中断线程
shutdown
shutdownnow
如何正确使用线程池
避免使用无界队列
明确拒绝任务时的行为
获取处理结果和异常
ExecutorService executorService = Executors.newFixedThreadPool(4);
Future<Object> future = executorService.submit(new Callable<Object>() {
@Override
public Object call() throws Exception {
throw new RuntimeException("exception in call~");// 该异常会在调用Future.get()时传递给调用者
}
});
try {
Object result = future.get();
} catch (InterruptedException e) {
// interrupt
} catch (ExecutionException e) {
// exception in Callable.call()
e.printStackTrace();
}
线程池的优势
降低资源消耗,创建和销毁线程很占用资源,jvm需要跟踪回收。
提高响应速度,任务到达,无需等待创建线程。
方便管理