【无标题】
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |
一、线程池有哪些优点
总体来说线程池有如下的优势
1降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
2提高响应速度。当任务到达时任务可以不需要等到线程创建就能立即执行。
3提高线程的可管理性。线程是稀缺资源如果无限制的创建不仅会消耗系统资源还会降低系统的稳定性使用线程池可以进行统一的分配调优和监控。
二、线程池的主要参数
线程池ThreadPoolExecutor
的继承关系
想知道有哪些参数先看参数最多的构造函数
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;
}
可以看到创建一个线程池需要七个参数。
- corePoolSize线程池核心线程数量大小有新任务进来时如果线程池中的线程数小于这个值则会创建新的线程来执行任务不管有没有空闲线程都会创建直到线程数量大于等于这个数量。
- maximumPoolSize线程池最大线程数量大小当达到核心线程数且队列任务已满会增加线程至最大线程数。
- keepAliveTime线程的最大空余时间大于这个时间将被回收线程数大于核心线程数时多余的线程空闲时长达到这个值就会被回收。
- unit空余时长的单位。
- workQueue阻塞队列当核心线程数已满任务会被放到这个队列中。
- threadFactory线程工厂线程池中的线程都是由这个线程工厂创建的线程池提供了默认的线程工厂。
- handler拒绝策略当队列任务已满且线程数量达到最大线程数新进入的任务会执行这个拒绝策略来选择丢弃哪个任务线程池提供了四种默认的拒绝策略。
线程工厂ThreadFactor
线程池中默认给定了一个线程工厂DefaultThreadFactory
线程工厂的作用是用于创建线程自己创建线程工厂时需要实现ThreadFactor
接口该接口中只有一个方法Thread newThread(Runnable r);
即创建线程的方法自定义线程工厂的好处是可以自定义线程名称。
阻塞队列BlockingQueue
阻塞队列是在核心线程满了以后存放任务使用常用的有LinkedBlockingQueue
、ArrayBlockingQueue
、SynchronousQueue
、DelayedWorkQueue
等当阻塞队列也满时会创建线程至最大线程数如果队列已满也达到最大线程数则执行拒绝策略。
拒绝策略RejectedExecutionHandler
- DiscardPolicy丢弃该任务不抛异常。
- DiscardOldestPolicy丢弃最早加入队列的任务不抛异常。
- AbortPolicy丢弃该任务并抛出异常
RejectedExecutionException
默认使用该策略。 - CallerRunsPolicy:由调用线程池的线程来执行当前任务。
查看源码
public static class DiscardPolicy implements RejectedExecutionHandler {
public DiscardPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
}
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
public DiscardOldestPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
}
public static class AbortPolicy implements RejectedExecutionHandler {
public AbortPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
}
public static class CallerRunsPolicy implements RejectedExecutionHandler {
public CallerRunsPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
}
如果想要实现自己的拒绝策略那么实现RejectedExecutionHandler
接口即可。
三、线程池的执行流程
如下图
查看源码执行流程execute()方法
public void execute(Runnable command) {
//传入的任务是否为空
if (command == null)
throw new NullPointerException();
/*
* Proceed in 3 steps:
*
* 1. 如果运行的线程数小于corePoolSize则创建新的线程线程池状态为正在运行的状态。
*
* 2. 如果任务排队成功仍然需要检查线程池的状态如果不是可运行的状态则回滚刚刚的操作。
*
* 3. 如果排队失败则尝试添加新的线程如果已经达到线程池最大数量则执行拒绝策略。
*/
//判断线程数是否小于核心线程数
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
//如果小于新增一个线程来执行
if (addWorker(command, true))
return;
c = ctl.get();
}
//如果核心线程数已满则向阻塞队列中添加任务
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
//如果添加失败则创建线程至最大值
else if (!addWorker(command, false))
//如果创建失败则执行拒绝策略
reject(command);
}
四、线程池的状态
查看源码发现线程池有五种状态如下
// runState is stored in the high-order bits
private static final int RUNNING = -1 << COUNT_BITS;
private static final int SHUTDOWN = 0 << COUNT_BITS;
private static final int STOP = 1 << COUNT_BITS;
private static final int TIDYING = 2 << COUNT_BITS;
private static final int TERMINATED = 3 << COUNT_BITS;
- RUNNING运行状态线程池创建完成后就是运行状态。
- SHUTDOWN关闭状态执行
shutdown()
方法后进入此状态继续处理队列中的任务但是不再接收新的任务。 - STOP停止状态
shutdownNow()
方法后进入此状态不处理队列中的任务也不接收新的任务。 - TIDYING整理状态运行的线程数为0队列中任务为空时则进入此状态进入此状态后会执行
terminated()
方法进入销毁状态。 - TERMINATED销毁状态执行
terminated()
方法进入此状态。
状态转换如下图
五、Java提供的快速创建的线程池
Executors
提供了几种快速创建的线程池
- newSingleThreadExecutor只有一个线程的线程池
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
- newFixedThreadPool固定线程数量的线程池
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
- newCachedThreadPool可缓存的线程池即不限制线程数量的线程池。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<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());
}
- newSingleThreadScheduledExecutor单个线程的定时线程池功能和上面一样。
public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
return new DelegatedScheduledExecutorService
(new ScheduledThreadPoolExecutor(1));
}