我们还是看一下它加锁的地方,就在很关键的 runWorker 方法里面:
那么问题就来了:
这里是线程池里面的线程,正在执行提交的任务的逻辑的地方,为什么需要加锁呢?
这里为什么又自己搞了一个锁,而不用已有的 ReentrantLock,即 mainLock 呢?
答案还是写在注释里面:
我知道你看着这么大一段英文瞬间就没有了兴趣 。
但是别慌,我带你细嚼慢咽 。
第一句话就开门见山的说了:
worker 类存在的主要意义就是为了维护线程的中断状态 。
维护的线程也不是一般的线程,是 running tasks 的线程,也就是正在运行的线程 。
怎么理解这个“维护线程的中断状态”呢?
你去看 Worker 类的 lock 和 tryLock 方法,都各自只有一个地方调用 。
lock 方法我们前面说了,在 runWorker 方法里面调用了 。
在 tryLock 方法是在这里调用的:
这个方法也是我们的老朋友了,前面刚刚才讲过,是用来中断线程的 。
中断的是什么类型的线程呢?
就是正在等待任务的线程,即在这里等着的线程:
换句话说:正在执行任务的线程是不应该被中断的 。
那线程池怎么知道那哪任务是正在执行中的,不应该被中断呢?
我们看一下判断条件:
关键的条件其实就是 w.tryLock() 方法 。
所以看一下 tryLock 方法里面的核心逻辑是怎么样的:
核心逻辑就是一个 CAS 操作,把某个状态从 0 更新为 1,如果成功了,就是 tryLock 成功 。
“0”、“1” 分别是什么玩意呢?
注释,答案还是在注释里面:
所以,tryLock 中的核心逻辑compareAndSetState(0, 1),就是一个上锁的操作 。
如果 tryLock 失败了,会是什么原因呢?
肯定是此时的状态已经是 1 了 。
那么状态什么时候变成 1 呢?
一个时机就是执行 lock 方法的时候,它也会调用 tryAcquire 方法 。
那 lock 是在什么时候上锁的呢?
runWorker 方法里面,获取到 task,准备执行的时候 。
也就是说状态为 1 的 worker 肯定就是正在执行任务的线程,不可以被中断 。
另外,状态的初始值被设置为 -1 。
我们可以写个简单的代码,验证一下上面的三个状态:
首先我们定义一个线程池,然后调用 prestartAllCoreThreads 方法把所有线程都预热起来,让它们处于等待接收任务的状态 。
你说这个时候,三个 worker 的状态分别是什么?
那必须得是 0,未上锁的状态 。
当然了,你也有可能看到这样的局面:
-1 是从哪里来的呢?
别慌,我等下给你讲,我们先看看 1 在哪呢?
按照之前的分析,我们只需要往线程池里面提交一个任务即可:
这个时候,假如我们调用 shutdown 呢,会发什么?
当然是中断空闲的线程了 。
那正在执行任务的这个线程怎么办呢?
因为是个 while 循环,等到任务执行完成后,会再次调用 getTask 方法:
getTask 方法里面会先判断线程池状态,这个时候就能感知到线程池关闭了,返回 null,这个 worker 也就默默的退出了 。
好了,前面说了这么多,你只要记住一个大前提:自定义 worker 类的大前提是为了维护中断状态,因为正在执行任务的线程是不应该被中断的 。
接着往下看注释:
这里解释了为什么老爷子不用 ReentrantLock 而是选择了自己搞一个 worker 类 。
因为他想要的是一个不能重入的互斥锁,而 ReentrantLock 是可以重入的 。
从前面分析的这个方法也能看出来,是一个非重入的方法:
传进来的参数根本没有使用,代码里面也没有累加的逻辑 。
推荐阅读
- 高筋面粉、中筋面粉、低筋面粉 面粉是什么做的
- 马斯克哀叹德国官僚作风:柏林工厂投产或再次延期
- css font-style属性的作用是什么
- 外部css样式表的作用是什么
- css的基本选择器有哪些
- 年年有余的鱼寓意是什么,吃鱼是年年有余的意思找其他相似的语言
- 广州三个值得一去的摩天轮 广州摩天轮
- 手机4G信号满格,上网速度却很慢 手机网速慢的原因
- 感恩节祝福语简短句子大全感谢祝福父母老师的句子
