Java线程池
1.线程池的使用场景
一般使用线程的方式 new Thread().start();
这样有两个问题:
-
线程的创建数量不可控,比如下面的例子中
size
这个值可能非常大for (int i = 0; i < size ; i++) { new Thread().start(); }
-
频繁的创建和销毁线程
线程池如何解决这些问题:
- 控制线程的数量,采用核心线程数,最大线程数,以及阻塞队列的设计
- 降低频繁的创建和销毁线程开销,线程的复用
- 使得对应任务的响应速度更快
2.Java中的线程池
线程池的核心参数
public ThreadPoolExecutor(int corePoolSize,//核心线程数
int maximumPoolSize,//最大线程数
long keepAliveTime,//超时时间,超出核心线程数以外的线程空余存活时间
TimeUnit unit,//超时时间单位
BlockingQueue<Runnable> workQueue,//执行任务线程的阻塞队列
ThreadFactory threadFactory,//线程工厂
RejectedExecutionHandler handler//任务无法执行的拒绝策略
) {...
线程池初始化时是没有创建线程的,线程池的线程初始化与其他线程一样,但是在完成任务之后,该线程不会自行销毁,而是以挂起的状态返回到线程池。直到应用再次向线程池发出请求时,线程池里挂起的线程就会再度激活执行任务。
这样既节省了建立线程所造成的性能损耗,也可以让多个任务反复重用同一线程,从而在应用程序生存期内节约大量开销。
Executors类API
Java
JUC
给我们提供了一些已经封装好的线程池工具,屏蔽了很多细节,使用更加方便。但是同时也是一把双刃剑,使用太多之后可能会忘记线程池的核心参数(所以在阿里巴巴的开发手册中不建议使用此类)
-
newFixedThreadPool
-
new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>())
-
核心线程数和最大线程数相等
nThreads
-
固定线程数量的线程池
-
当有一个任务提交时,如果线程池中的线程空闲,则立即执行;如果没有会被暂缓在任务队列中等待
-
-
newCachedThreadPool
-
new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>())
-
根据实际情况调整线程个数的线程池,核心线程数为 0,但不限制最大线程数量
-
如果有空闲的线程则执行任务,没有任务不创建线程。并且每个空闲线程会在60秒后自动回收
-
-
newSingleThreadExecutor
-
new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>())
-
创建一个核心线程的线程池,在空闲时执行,如果没有空闲线程,多余线程在任务队列中等待
-
-
newScheduledThreadPool
- 指定线程的个数,但是这个线程池还带有延迟和周期性执行任务的功能,类似定时器
-
newWorkStealingPool
- fork/join,这个是 JDK1.8 版本加入的一种线程池,stealing 翻译为抢断、窃取的意思,它实现的一个线程池和上面 4 种都不一样,用的是 ForkJoinPool 类
- 最明显的用意就是它是一个并行的线程池,参数中传入的是一个线程并发的数量,这里和之前就有很明显的区别,前面 4 种线程池都有核心线程数、最大线程数等等,而这就使用了一个并发线程数解决问题。从介绍中,还说明这个线程池不会保证任务的顺序执行,也就是 WorkStealing 的意思,抢占式的工作
FixedThreadPool分析
public class ThreadPoolDemo implements Runnable {
static ExecutorService executorService = Executors.newFixedThreadPool(3);
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
executorService.execute(new ThreadPoolDemo());
}
}
@Override
public void run() {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
String name = Thread.currentThread().getName();
System.out.println(name);
}
}
执行结果
通过代码的执行效果可以看到,线程一直只有 thread-1/thread-2/thread-3 三个线程在执行
通过现象看本质
-
FixedThreadPool 的核心线程数和最大线程数都是指定值,也就是说当线程池中的线程数超过核心线程数后,任务都会被放到阻塞队列中。
-
keepAliveTime
为 0L,超出核心线程数量以外的线程空余存活时间,保证只有核心线程存在 -
阻塞队列是
LinkedBlockingQueue
,默认的容量是Integer.MAX_VALUE,相当于没有上限
此线程池执行任务的流程如下:
-
线程少于核心线程数,新建线程执行任务
-
线程数大于核心线程数,将任务加入到阻塞队列
-
阻塞队列没有限制,可以一直添加
-
线程执行完当前任务,空闲时会从队列中获取任务执行
CachedThreadPool
它的执行流程如下:
-
没有核心线程,直接向
SynchronousQueue
阻塞队列中提交任务SynchronousQueue
没有容量,是无缓冲等待队列,不存储元素的阻塞队列, 每一个 put 操作必须等待一个 take 操作,否则不能继续添加元素。 -
如果有空闲线程,就去取出任务执行;如果没有空闲线程,就新建一个
-
执行完任务的线程有
60
秒生存时间,如果在这个时间内可以接到新任务,就可以继续活下去,否则就被回收
3.源码分析
ThreadPoolExecutor#execute
执行逻辑图:
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
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);
}
ctl 核心变量
ctl
是一个原子类,主要作用是用来保存线程数量和线程池的状态。
一个int
类型是32
个bit
位,高3
位表示线程池的状态,低29
位表示线程数量。
以 RUNNING 为例
-1 二进制 = 1111 1111 1111 1111 1111 1111 1111 1111
左移29位 = 1110 0000 0000 0000 0000 0000 0000 0000
通过runStateOf()
方法计算获得状态
通过workerCountOf()
方法计算获得线程数量
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;//32-3 = 29
//将1左移29位,再-1表示最大线程容量
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// runState is stored in the high-order bits 运行状态保存在高3位
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;//所有任务都已经结束,线程数量为0.处于该状态线程池即将调用terminated()方法
private static final int TERMINATED = 3 << COUNT_BITS;//terminated()方法执行完成
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; }//当wc为0时得到的还是原值
线程池状态变化
addWorker()
- 第一段代码主要是通过自旋的方式添加工作线程数
- 第二段代码是创建工作线程
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
//第一段代码增加工作线程数
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);//获取当前状态
// 1.状态>=SHUTDOWN 只有 4中状态 SHUTDOWN STOP TIDYING TERMINATED 都表示线程池将要关闭,不允许增加worker
// 2.SHUTDOWN状态不接受新任务,但仍然会执行已经加入任务队列的任务,所以当进入SHUTDOWN状态,而传进来的任务为空,并且任务队列不为空的时候,是允许添加新线程的,如果把这个条件取反,就表示不允许添加worker
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {//通过自旋的方式添加工作线程数
int wc = workerCountOf(c);//当前工作线程数
//判断当前工作线程数>=最大线程数 或者 >=核心线程数(当core = true)
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
if (compareAndIncrementWorkerCount(c))//cas增加工作线程数
break retry;//成功退出循环
c = ctl.get(); // Re-read ctl
if (runStateOf(c) != rs)//如果状态不相等发生变化
continue retry;//重新进入自旋
// else CAS failed due to workerCount change; retry inner loop
}
}
//第二段代码是创建工作线程
boolean workerStarted = false;//工作线程是否启动的标识
boolean workerAdded = false;//工作线程是否已经添加成功的标识
Worker w = null;
try {
//firstTask 是我们传入的实现了runnable接口的对象实例
w = new Worker(firstTask);//创建一个新的worker
final Thread t = w.thread;//从 worker 对象中取出线程
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();//加锁 避免并发问题
try {
// Recheck while holding lock.
// Back out on ThreadFactory failure or if
// shut down before lock acquired.
int rs = runStateOf(ctl.get());//获取当前状态
//rs<SHUTDOWN 表示正常运行状态
//SHUTDOWN状态且firstTask为空
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive()) // 如果工作线程还存活,理论上不可能存活
throw new IllegalThreadStateException();
workers.add(w);//否则添加workers HashSet集合中
int s = workers.size();
if (s > largestPoolSize)//如果集合大小>曾经出现最大线程数
largestPoolSize = s;//更新这个最大线程数
workerAdded = true;//添加成功标识
}
} finally {
mainLock.unlock();//释放锁
}
if (workerAdded) {//如果添加成功
t.start();//启动线程
workerStarted = true;//设置线程开启状态成功
}
}
} finally {
if (! workerStarted)//线程启动失败
addWorkerFailed(w);//添加失败任务,回滚相应的操作
}
return workerStarted;
}
线程复用
- 假设线程池有三个核心线程
- 线程池会创建三个
Worker
自循环线程 一直执行(当然也有退出条件) new App()
作为task
传入到Worker
中,Worker
中会通过工厂模式创建一个Thread
t.start()
- >Worker
执行runWorker
方法,最后调用task.run()
执行方法- 如果三个
Worker
都在执行任务,其他任务会放到workQueue
阻塞队列中 - 当三个核心线程执行完,会通过
getTask()
方法从workQueue
阻塞队列中获取要执行的任务继续执行 - 一直如此循环,直到
task = getTask() == null
- 通过上述的方式只使用三个核心自循环线程就可以执行完100个任务,减少了线程频繁创建和销毁
Worker
1.Worker 实现了 Runnable 接口,每个 Worker 其实都是一条线程,同时里面包含了一个 firstTask
,即初始化时要被首先执行的任务
2.Worker 中有一个thread
属性,在构造方法初始化的时候,通过工厂类创建赋值,addWroker()
中的t.start()
最终执行的是Worker.runWorker()
方法
3.Worker 继承了 AQS,使用 AQS 来实现独占锁的功能。为什么不使用 ReentrantLock 来实现呢?可以看到 tryAcquire 方法,它是不允许重入的,而 ReentrantLock 是允许重入的;lock 方法一旦获取了独占锁,表示当前线程正在执行任务中;那么它会有以下几个作用
-
如果正在执行任务,则不应该中断线程
-
如果该线程现在不是独占锁的状态,也就是空闲的状态,说明它没有在处理任务,这时可以对该线程进行中断
-
线程池在执行 shutdown 方法或 tryTerminate 方法时会调用 interruptIdleWorkers 方法来中断空闲的线程,interruptIdleWorkers 方法会使用 tryLock 方法来判断线程池中的线程是否是空闲状态
-
之所以设置为不可重入,是因为我们不希望任务在调用像 setCorePoolSize 这样的线程池控制方法时重新获取锁,这样会中断正在运行的线程
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
private static final long serialVersionUID = 6138294804551838833L;
/** Thread this worker is running in. Null if factory fails. */
final Thread thread;//这个线程才是真正执行task的线程
/** Initial task to run. Possibly null. */
Runnable firstTask;//这就是需要执行的task
/** Per-thread task counter */
volatile long completedTasks;//完成的任务数,用于线程池统计
Worker(Runnable firstTask) {
setState(-1); //初始状态 -1,防止在调用 runWorker(),也就是真正执行task前中断 thread
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);//创建线程
}
/** Delegates main run loop to outer runWorker */
public void run() {
runWorker(this);
}
protected boolean isHeldExclusively() {
return getState() != 0;
}
protected boolean tryAcquire(int unused) {//竞争锁
if (compareAndSetState(0, 1)) {//cas设置状态为1,表示获得锁,设置OwnerThread=当前线程
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
protected boolean tryRelease(int unused) {//释放锁
setExclusiveOwnerThread(null);//设置OwnerThread=null
setState(0);//设置状态为0,表示无锁
return true;
}
public void lock() { acquire(1); }
public boolean tryLock() { return tryAcquire(1); }
public void unlock() { release(1); }
public boolean isLocked() { return isHeldExclusively(); }
void interruptIfStarted() {
Thread t;
if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
}
}
}
}
state状态
state值 | 意义 |
---|---|
-1 | 初始状态 |
0 | 无锁 |
1 | 获得锁 |
addWorkerFailed()
如果
AddWorker
失败了,回滚之前添加worker
的操作1.如果
worker
不为空,从wokers hashSet
集合中移除这个worker
2.
cas
的方式工作线程数-1
3.尝试结束线程池
private void addWorkerFailed(Worker w) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();//加锁
try {
if (w != null)//如果work不为空 表示添加成功
workers.remove(w);//删除当前work
decrementWorkerCount();//工作线程数-1
tryTerminate();//尝试结束线程池
} finally {
mainLock.unlock();//释放锁
}
}
tryTerminate()
尝试结束线程池
final void tryTerminate() {
for (;;) {//自旋
int c = ctl.get();
//1.如果状态为运行状态
//2.或者状态为TIDYING
//3.或者状态为SHUTDOWN 并且 阻塞队列不为空
if (isRunning(c) ||
runStateAtLeast(c, TIDYING) ||
(runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
return;//不关闭线程池
if (workerCountOf(c) != 0) { // 工作线程数不为0
interruptIdleWorkers(ONLY_ONE);//中断线程
return;//不关闭线程池
}
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {//cas设置ctl的状态为TIDYING
try {
terminated();//空方法
} finally {
ctl.set(ctlOf(TERMINATED, 0));//设置ctl的状态为TERMINATED
termination.signalAll();//唤醒所有被termination.await阻塞的线程
}
return;
}
} finally {
mainLock.unlock();
}
// else retry on failed CAS
}
}
interruptIdleWorkers()
中断空闲的
worker
线程
private void interruptIdleWorkers(boolean onlyOne) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers) {//循环workers集合
Thread t = w.thread;//获取worker中的t线程
if (!t.isInterrupted() && w.tryLock()) {//尝试获取lock
try {
t.interrupt();//中断线程
} catch (SecurityException ignore) {
} finally {
w.unlock();//释放锁 这个锁在runWorker方法中获取
}
}
if (onlyOne)
break;
}
} finally {
mainLock.unlock();
}
}
runWorker()
1.自循环线程
2.通过
getTask()
方法不断从阻塞队列中获得任务,通过task.run()
方法执行3.while 循环执行完毕以后,在 finally 中会调用 processWorkerExit,来销毁工作线程
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // 释放锁 设置work的state=0 允许中断
boolean completedAbruptly = true;
try {
//一直执行 如果task不为空 或者 从队列中获取的task不为空
while (task != null || (task = getTask()) != null) {
w.lock(); //上锁,不是为了防止并发执行任务,为了在shutdown()时不终止正在运行的 worker线程
//线程池为stop状态时不接受新任务,不执行已经加入任务队列的任务,还中断正在执行的任务
//确保线程中断标志位为 true 且是 stop 状态以上,接着清除了中断标志
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();//中断当前任务
try {
beforeExecute(wt, task);//空实现,需要自己继承,可用于线程池监控
Throwable thrown = null;
try {
task.run();//执行task中的run方法
} catch (RuntimeException x) {
thrown = x; throw x;
} catch (Error x) {
thrown = x; throw x;
} catch (Throwable x) {
thrown = x; throw new Error(x);
} finally {
afterExecute(task, thrown);//空实现,需要自己继承,可用于线程池监控
}
} finally {
//置空任务(这样下次循环开始时,task 依然为 null,需要再通过 getTask()取)
task = null;
w.completedTasks++;//记录该 Worker 完成任务数量
w.unlock();//解锁
}
}
completedAbruptly = false;
} finally {
//1.将 worker 从数组 workers 里删除掉
//2.根据布尔值 allowCoreThreadTimeOut 来决定是否补充新的 Worker 进数组workers
processWorkerExit(w, completedAbruptly);
}
}
getTask()
1.从阻塞队列
workQueue
中拿数据
2.非核心线程释放
private Runnable getTask() {
boolean timedOut = false; // Did the last poll() time out?
for (;;) {//自旋
int c = ctl.get();
int rs = runStateOf(c);//获取线程池状态
// 单线程池关闭时,阻塞队列是否为空
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();//工作线程数-1
return null;//跳出上一层runWorker的循环
}
int wc = workerCountOf(c);//获取当前工作线程数
// 1.allowCoreThreadTimeOut 设置为true,表示核心线程数也会被回收
// 2.或者当前线程数 > 核心线程数
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
// 1.工作线程数>maximumPoolSize,可能是线程池在运行时被调用了 setMaximumPoolSize()被改变了大小,否则已经 addWorker()成功不会超过 maximumPoolSize
// 2.timed && timedOut 如果为 true,表示当前操作需要进行超时控制,并且上次从阻塞队列中获取任务发生了超时.其实就是体现了空闲线程的存活时间
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))//cas设置当前线程数-1
return null;//返回上一层 runWorker方法中while()退出条件,getTask()=null
continue;
}
try {
//如果timed为true,通过poll来控制超时时间获取阻塞队列任务,如果超时则r=null
//否则通过take来获取阻塞队列中的任务
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)//如果不为空,返回给runWorker方法继续处理
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
processWorkerExit()
1.将
worker
从数组workers
里删除掉
2.根据布尔值allowCoreThreadTimeOut
来决定是否补充新的worker
进HashSet
数组workers
private void processWorkerExit(Worker w, boolean completedAbruptly) {
//一般情况下completedAbruptly为false 机runworker正常执行并退出 异常情况下 true
if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
decrementWorkerCount();//工作线程数-1
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
completedTaskCount += w.completedTasks;//用做线程池的监控
workers.remove(w);//将 worker 从数组 workers 里删除掉
} finally {
mainLock.unlock();
}
tryTerminate();//尝试结束线程池
int c = ctl.get();
if (runStateLessThan(c, STOP)) {//如果线程池还未到STOP状态
if (!completedAbruptly) {
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
if (min == 0 && ! workQueue.isEmpty())//阻塞队列不为空
min = 1;
if (workerCountOf(c) >= min)//工作线程数>核心线程数 或者 大于1
return; // replacement not needed
}
addWorker(null, false);//增加一个空的非核心线程,如果还有任务没有执行,会在此工作线程执行
}
}
接着回到ThreadPoolExecutor#execute
,上面主要分析的是addWorker()
增加核心工作线程和非核心工作线程,Worker
类如何实现线程的复用,runWorker()
如何执行任务。接下来我们看看当最大线程数和阻塞队列都满了,线程池的拒绝策略。
reject()
四种拒绝策略实现类
1.AbortPolicy: 直接抛出异常,默认策略。
2.CallerRunsPolicy:用调用者所在的线程来执行任务。
3.DiscardOldestPolicy:丢弃阻塞队列中最靠前的任务,并执行当前任务。
4.DiscardPolicy:直接丢弃任务。
这四个实现都比较简单,就不贴出源码进行分析了。
线程池关闭
ThreadPoolExecutor 提供了两个方法,用于线程池的关闭
shutdown()
:不会立即终止线程池,而是要等所有任务缓存队列中的任务都执行完后才终止,但再也不会接受新的任务
shutdownNow()
:立即终止线程池,并尝试打断正在执行的任务,并且清空任务缓存队列,返回尚未执行的任务
shutdown()
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(SHUTDOWN);//设置线程池的状态
interruptIdleWorkers();//中断空闲工作线程,中断前会进行一些判断
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
tryTerminate();
}
shutdownNow()
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(STOP);//设置线程池的状态
interruptWorkers();//强制中断工作线程
tasks = drainQueue();
} finally {
mainLock.unlock();
}
tryTerminate();//尝试结束线程池
return tasks;//返还阻塞队列中还存在的任务
}
4.线程池监控
如果在项目中大规模的使用了线程池,那么必须要有一套监控体系,来指导当前线程池的状态,当出现问题的时候可以快速定位到问题;通过重写
shutdown()
,beforeExecute()
,afterExecute()
等方法可以实现对线程的监控。
public class ThreadPoolMonitor extends ThreadPoolExecutor {
private ConcurrentHashMap<String, Date> startTimes;
public ThreadPoolMonitor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
this.startTimes = new ConcurrentHashMap<>();
}
@Override
public void shutdown() {
System.out.printf("已经执行的任务数:[%s]\n当前活动的线程数:[%s]\n当前排队的任务数:[%s]", this.getCompletedTaskCount(), this.getActiveCount(), this.getQueue().size());
super.shutdown();
}
@Override
protected void beforeExecute(Thread t, Runnable r) {
startTimes.put(String.valueOf(r.hashCode()),new Date());
super.beforeExecute(t, r);
}
@Override
protected void afterExecute(Runnable r, Throwable t) {
Date startDate = startTimes.remove(String.valueOf(r.hashCode()));
Date finishDate = new Date();
long diff = finishDate.getTime() - startDate.getTime();
// 统计任务耗时、初始线程数、核心线程数、正在执行的任务数量、
// 已完成任务数量、任务总数、队列里缓存的任务数量、
// 池中存在的最大线程数、最大允许的线程数、线程空闲时间、线程池是否关闭、线程池是否终止
System.out.print("任务耗时:"+diff+"\n");
System.out.print("初始线程数:"+this.getPoolSize()+"\n");
System.out.print("核心线程数:"+this.getCorePoolSize()+"\n");
System.out.print("正在执行的任务数量:"+this.getActiveCount()+"\n");
System.out.print("已经执行的任务数:"+this.getCompletedTaskCount()+"\n");
System.out.print("任务总数:"+this.getTaskCount()+"\n");
System.out.print("最大允许的线程数:"+this.getMaximumPoolSize()+"\n");
System.out.print("线程空闲时间:"+this.getKeepAliveTime(TimeUnit.MILLISECONDS)+"\n");
super.afterExecute(r, t);
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolMonitor(3, 5, 0L, TimeUnit.SECONDS, new
LinkedBlockingQueue<> ());
}
}
测试Demo
public class ThreadPoolMonitorDemo implements Runnable {
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = ThreadPoolMonitor.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
executorService.execute(new ThreadPoolMonitorDemo());
}
executorService.shutdown();
}
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
执行结果:
5.非核心Worker工作线程创建如何回收?
getTask() 方法中的这一段
1.满足条件 wc > corePoolSize 此时工作线程数 > 核心线程数
timed = true
2.那么
r = workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS)
通过poll
获取阻塞队列的任务,如果在keepAliveTime
时间内获取到任务,执行任务3.如果没有获取到任务,返回
null
,退出runWorker
的自循环线程,执行完线程,JVM进行回收
// 满足条件 wc > corePoolSize 此时工作线程数>核心线程数 timed = true
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
...
try {
//那么poll从阻塞队列中获取任务,超时返回null
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
6.参考
腾讯课堂->咕泡学院->mic老师->并发编程基础