Java 线程池ThreadPoolExecutor
1、线程池的作用
我们清楚当某一个功能需要用到多线程的时候,传统的实现方法需要每次都去创建和销毁线程,这样如果单位时间内线程数量很多,导致创建和销毁会暂用大量的时间和空间,针对该问题,后续引出了线程池,线程池的作用就是能统一把线程放到一起管理,并且用几乎很少的线程创建和销毁来管理一堆的线程。
2、线程池
线程池的创建
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());
}
这里也就举出一个方法的例子来进行之后的讲解吧,我们可以看出,Executors只是个工厂而已,方法也只是来实例化不同的对象,实际上实例化出来的关键类就是ThreadPoolExecutor。现在我们就先来简单地对ThreadPoolExecutor构造函数内的每个参数进行解释一下吧。
corePoolSize(核心线程池大小):当提交一个任务到线程池时,线程池会创建一个线程来执行任务,即使其他空闲的基本线程能够执行新任务也会创建线程,当任务数大于核心线程数的时候就不会再创建。在这里要注意一点,线程池刚创建的时候,其中并没有创建任何线程,而是等任务来才去创建线程,除非调用了prestartAllCoreThreads()或者prestartCoreThread()方法 ,这样才会预先创建好corePoolSize个线程或者一个线程。
maximumPoolSize(线程池最大线程数):线程池允许创建的最大线程数,如果队列满了,并且已创建的线程数小于最大线程数,则线程池会再创建新的线程执行任务。值得注意的是,如果使用了无界队列,此参数就没有意义了。
keepAliveTime(线程活动保持时间):此参数默认在线程数大于corePoolSize的情况下才会起作用, 当线程的空闲时间达到keepAliveTime的时候就会终止,直至线程数目小于corePoolSize。不过如果调用了allowCoreThreadTimeOut方法,则当线程数目小于corePoolSize的时候也会起作用.
unit(keelAliveTime的时间单位):keelAliveTime的时间单位,一共有7种,在这里就不列举了。
3、源码
//初始化-536870912。存放线程池的有效线程数和线程池的运行状态
/**
* In order to pack them into one int, we limit workerCount to
* (2^29)-1 (about 500 million) threads rather than (2^31)-1 (2
* billion) otherwise representable. If this is ever an issue in
* the future, the variable can be changed to be an AtomicLong,
* and the shift/mask constants below adjusted. But until the need
* arises, this code is a bit faster and simpler using an int.
*/
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
//线程池状态码
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// runState is stored in the high-order bits
//可以看出状态码是-1 0 1 2 3 位移29位
//可以看出running的状态小于0 -1位移29位为负整数,每次添加一个线程,做++操作。
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;
// Packing and unpacking ctl
private static int runStateOf(int c) { return c & ~CAPACITY; }
private static int workerCountOf(int c) { return c & CAPACITY; }
private static int ctlOf(int rs, int wc) { return rs | wc; }
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
* Proceed in 3 steps:
*
* 1. If fewer than corePoolSize threads are running, try to
* start a new thread with the given command as its first
* task. The call to addWorker atomically checks runState and
* workerCount, and so prevents false alarms that would add
* threads when it shouldn't, by returning false.
*
* 2. If a task can be successfully queued, then we still need
* to double-check whether we should have added a thread
* (because existing ones died since last checking) or that
* the pool shut down since entry into this method. So we
* recheck state and if necessary roll back the enqueuing if
* stopped, or start a new thread if there are none.
*
* 3. If we cannot queue task, then we try to add a new
* thread. If it fails, we know we are shut down or saturated
* and so reject the task.
*/
//AtomicInteger存
int c = ctl.get();
//工作线程小于corePoolSize
if (workerCountOf(c) < corePoolSize) {
//添加一个core线程,此处参数为true,表示添加的线程是core容量下的线程
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);
//线程池在运行,有效线程数为0
else if (workerCountOf(recheck) == 0)
//添加一个空线程进线程池,使用非core容量线程
//仅有一种情况,会走这步,core线程数为0,max线程数>0,队列容量>0
//创建一个非core容量的线程,线程池会将队列的command执行
addWorker(null, false);
}
//线程池停止了或者队列已满,添加maximumPoolSize容量工作线程,如果失败,执行拒绝策略
else if (!addWorker(command, false))
reject(command);
}
final void reject(Runnable command) {
handler.rejectedExecution(command, this);
}
不管是ThreadPoolExecutor还是spring boot所提供的ThreadPoolTaskExecutor最终都是继承了Executor,并且execute以及submit的方法最后都是去执行Executor里面的execute方法。
上面的步骤中大致为:
1) 检查core线程池数量<corePoolSize数量,是,可以提交任务或新建线程执行任务 。
2)如果corePoolSize线程数量已使用,如果队列容量未满,则加入队列。
3)队列已满,创建maximumPoolSize线程数量执行;如果失败则执行关闭线程池或者拒绝策略。
在上面得代码中有一个addWorker方法,这个方法里面有一个Worker对象,在该对象里面其实就是重新实现了execute方法。
而在线程池中用到的submit方法只不过是在调用的execute方法的基础上,只是多了newTaskFor,用来收集线程的运算结果。
总结
1)线程池优先使用corePoolSize的数量执行工作任务
2)如果超过corePoolSize,队列入队
3)超过队列,使用maximumPoolSize-corePoolSize的线程处理,这部分线程超时不干活就销毁掉。
4)每个线程执行结束的时候,会判断当前的工作线程和任务数,如果任务数多,就会创建空线程从队列拿任务。
5)线程池执行完成,不会自动销毁,需要手工shutdown,修改线程池状态,中断所有线程。