时隔上一篇技术文章更新差不多有 3 个星期了,起因的话在上一篇文章中写啦。废话不多说,开始咱们的线程池源码的第二轮浏览。
回顾
简略回顾下上一篇线程池源码中波及的两个办法,一个是execute()
执行工作的入口,还有一个是addWorker()
最艰深地了解就是是否须要增加新线程。而在addWoker()
的开端有这样一段代码
if (workerAdded) {t.start();
workerStarted = true;
}
显著地看到这里通过 start()
办法开启了多线程,而如果想要看线程的执行逻辑,就须要去到对应类中查看 run 办法,这里的 t 就是Worker
类外面的一个成员变量,所以 重点要看Worker
类中的run()
办法。
runWorker()
run()
办法的源码如图所示,最初是到了runWorker()
间接来看 runWorker 的源码
- 开始是一个循环,要么执行 worker 自带的第一个工作(firstTask),要么通过
getTask()
获取工作 -
有工作首先得保障线程池是失常的,以下两种状况均调用
wt.interrupt()
给 线程设置中断标记位- 线程池处于 STOP 状态,也就是不承受新工作,也不执行队列中的工作
- 如果线程的标记位曾经为 true,那么分明标记位,此时的线程池状态为 STOP 状态,这里看起来可能比拟顺当,有了第一种状况为什么还要第二种,不了解的能够先略过,前面会讲的。
- 失常状况下是调用
beforeExecute()
和afterExecute()
包裹者task.run()
看一下是如何 自定义前置和后置执行逻辑
因为是换电脑写了,所以例子可能和前一篇文章的不齐全一样,然而表白的是同一个意思
public class ThreadPoolExamples {public static void main(String[] args) {ThreadPoolExecutor executor = new MyThreadPoolExecutor(5, 5, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());
MyThread myThread = new MyThread();
executor.execute(myThread);
}
}
class MyThread extends Thread {
@Override
public void run() {System.out.println(Thread.currentThread().getName() + "is running");
}
}
class MyThreadPoolExecutor extends ThreadPoolExecutor {
@Override
protected void beforeExecute(Thread t, Runnable r) {System.out.println("【" + Thread.currentThread().getName() + "custom before execute】");
}
@Override
protected void afterExecute(Runnable r, Throwable t) {System.out.println("【" + Thread.currentThread().getName() + "is done】");
}
MyThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}
}
首先就是要创立本人的MyThreadPoolExecutor
类,继承ThreadPoolExecutor
,而后重写beforeExecute()
和afterExecute()
定义本人的逻辑即可,看下测试后果
能够看到前置和后置都曾经依照既定的逻辑在运行了,乏味的是,22 分钟过来了(不要问我为什么这么久,拿外卖吃货色去了)线程池还是没有停,为什么会这样呢。
留神后面剖析runWorker()
第一步的时候是一个循环,而后通过 firstTask 或者getTask()
获取工作,如果两种形式都获取不到工作,线程池就应该退出,看来神秘在getTask()
中。
getTask()
次要目标顾名思义就是获取到须要执行的工作,间接看源码
-
一进来也是一个死循环,能够先聚焦 什么时候会退出循环 ,必定是 不失常的状况 下会退出
- 当线程池状态不处于 RUNNING 或者 SHUTDOWN 的时候,或者是当线程处于 SHUTDOWN 然而工作队列中没有工作
- 当 wc 大于最大线程数并且工作队列为空的时候,或者当 wc 大于外围线程数并且 timedOut 为 true 并且外围队列为空的时候,或者如果设置了 allowCoreThreadTimeOut,并且 wc > 1 或者外围队列为空的时候
- 除了不失常的状况,接下来就是从工作队列中获取工作,不过是依据 timed 的来决定是用
poll()
还是take()
。 - 如果能取出工作,就间接返回工作;如果没有工作,要么超时设置 timedOut 为 ture,要么是抛出异样重置 timedOut 为 false。
能够看到,只有上述不失常的状况下退出循环,工作返回 null,进而导致
runWorker()
中的 while 循环退出,最初整个线程池敞开。否则都是会始终在getTask()
这里死循环。
到这里,为什么说线程池可能节俭资源呢,是因为其实它创立的线程的耗费只是体现在了 Worker 类的创立中,把其它要实现的工作放在工作队列外面,而后getTask()
获取工作,最初执行工作(调用task.run()
)
回绝策略
这个是在最开始execute()
的时候调用的
具体是如何请看上面的动图
能够看到,最初是调用了RejectedExecutionHandler
接口中的rejectedExecution(Runnable r, ThreadPoolExecutor executor);
办法,而后默认是有 4 种实现形式
有凋谢的接口,那必定是能自定义实现类的
class MyRejectedExecutionHandler implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {System.out.println("task is rejected");
}
}
该说的办法也根本都说了,用到的工作队列(BlockingQueue)在前面会另说,下一篇文章就总结下 jdk 的线程池啦!
创作不易,如果对你有帮忙,欢送点赞,珍藏和分享啦!
上面是集体公众号,有趣味的能够关注一下,说不定就是你的宝藏公众号哦,根本 2,3 天 1 更技术文章!!!