线程池总结
前言
使用线程池的好处
- 降低资源消耗 降低重复创建和销毁线程的资源消耗。
- 提高响应速度 任务到达时不需要等待线程创建。
- 提高线程的可管理性。使用线程池可以对线程池进行统一的分配,调优,和监控。
线程池流程
线程池的使用
创建
使用ThreadPoolExecute()
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler){
}
- corePoolSize:核心线程数
- maximumPoolSize:线程池最大线程数(最大线程数一般设为2N+1最好)
- keepAliveTime:工作线程空闲后存活时间。
- unit:keepAliveTime的单位
- workQueue:等待队列。(队列类型见下文)
- threadFactory:用于设置创建线程的工厂
- handler:饱和策略
阻塞队列类型
- ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按FIFO(先进先出)原则对元素进行排序。
- LinkedBlockingQueue:一个基于链表结构的阻塞队列,此队列按FIFO排序元素,吞吐量通常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列。
- SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于Linked-BlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了这个队列。
- PriorityBlockingQueue:一个具有优先级的无限阻塞队列。
饱和策略
-
AbortPolicy:直接抛出异常
-
CallerRunsPolicy:只用调用者所在线程来运行任务
-
DiscardOldestPolicy:丢弃队列中最旧的一个任务,并执行当前任务。(队列头头的被丢弃)
-
DiscardPolicy:不处理,丢弃掉。(丢掉最新的)
通常而言,这四种拒绝策略我们一般都不太适用我们的业务场景,我们一般都会自定义自己的拒绝策略,将线程任务放进redis或者mq消息队列中。
Executors提供的四种线程池
newFixedThreadPool 创建固定数目线程的线程池。
public static ExecutorService newFixedThreadPool(int nThreads){
return new ThreadPoolExecutor(nThreads, nThreads, 0L,
TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
}
- FixedThreadPool是一种容量固定的线程池;
- 阻塞队列采用LinkedBlockingQueue,它是一种无界队列;
- 由于阻塞队列是一个无界队列,因此永远不可能拒绝执行任务;
- 由于采用无界队列,实际线程数将永远维持在nThreads,因此maximumPoolSize和keepAliveTime将无效。
newCachedThreadPool 可缓存的线程池
public static ExecutorService newCachedThreadPool(){
return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
- CachedThreadPool是一种可以无限扩容的线程池;
- CachedThreadPool比较适合执行时间片比较小的任务;
- keepAliveTime为60,意味着线程空闲时间超过60s就会被杀死;
- 阻塞队列采用SynchronousQueue,这种阻塞队列没有存储空间,意味着只要有任务到来,就必须得有一个工作线程来处理,如果当前没有空闲线程,那么就再创建一个新的线程。
newSingleThreadExecutor 单线程化的线程池
public static ExecutorService newSingleThreadExecutor(){
return new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
- 只会创建一个工作线程来处理任务。
newScheduledThreadPool
public static ScheduledExecutorService
newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
- ScheduledThreadPool接收SchduledFutureTask类型的任务,提交任务的方式有2种;
- scheduledAtFixedRate;
- scheduledWithFixedDelay;
- SchduledFutureTask接收参数:
time:任务开始时间
sequenceNumber:任务序号
period:任务执行的时间间隔 - 阻塞队列采用DelayQueue,它是一种无界队列;
- DelayQueue内部封装了一个PriorityQueue,它会根据time的先后排序,若time相同,则根据sequenceNumber排序;
- 工作线程执行流程:
- 工作线程会从DelayQueue中取出已经到期的任务去执行;
- 执行结束后重新设置任务的到期时间,再次放回DelayQueue。