为什么不直接搞一个线程安全的 Set 集合,比如用这个玩意 Collections.synchronizedSet?
答案其实在前面已经出现过了,只是我没有特意说,大家没有注意到 。
就在 mainLock 的注释上写着:
我捡关键的地方给你说一下 。
首先看这句:
这句话是个倒装句,应该没啥生词,大家都认识 。
其中有个 it turns out to be,可以介绍一下,这是个短语,经常出现在美剧里面的对白 。
翻译过来就是四个字“事实证明” 。
所以,上面这整句话就是这样的:虽然我们可以使用某种并发安全的 set 集合,但是事实证明,一般来说,使用锁还是比较好的 。
接下来老爷子就要解释为什么用锁比较好了 。
我翻译上这句话的意思就是我没有乱说,都是有根据的,因为这是老爷子亲自解释的为什么他不用线程安全的 Set 集合 。
第一个原因是这样说的:
英文是的,我翻译成中文,加上自己的理解是这样的 。
首先第一句里面有个 “serializes interruptIdleWorkers”,这两个单词组合在一起还是有一定的迷惑性的 。
serializes 在这里,并不是指我们 Java 中的序列化操作,而是需要翻译为“串行化” 。
interruptIdleWorkers,这玩意根本就不是一个单词,这是线程池里面的一个方法:
在这个方法里面进来第一件事就是拿 mainLock 锁,然后尝试去做中断线程的操作 。
由于有 mainLock.lock 的存在,所以多个线程调用这个方法,就被 serializes 串行化了起来 。
串行化起来的好处是什么呢?
就是后面接着说的:避免了不必要的中断风暴(interrupt storms),尤其是调用 shutdown 方法的时候,避免退出的线程再次中断那些尚未中断的线程 。
为什么这里特意提到了 shutdown 方法呢?
因为 shutdown 方法调用了 interruptIdleWorkers:
所以上面啥意思呢?
这个地方就要用一个反证法了 。
假设我们使用的是并发安全的 Set 集合,不用 mainLock 。
这个时候有 5 个线程都来调用 shutdown 方法,由于没有用 mainLock,所以没有阻塞,那么每一个线程都会运行 interruptIdleWorkers 。
所以,就会出现第一个线程发起了中断,导致 worker,即线程正在中断中 。第二个线程又来发起中断了,于是再次对正在中断中的中断发起中断 。
额,有点像是绕口令了 。
所以我打算重复一遍:对正在中断中的中断,发起中断 。
因此,这里用锁是为了避免中断风暴(interrupt storms)的风险 。
并发的时候,只想要有一个线程能发起中断的操作,所以锁是必须要有的 。有了锁这个大前提后,反正 Set 集合也会被锁起来,索性就不需要并发安全的 Set 了 。
所以我理解,在这里用 mainLock 来实现串行化,同时保证了 Set 集合不会出现并发访问的情况 。
只要保证这个这个 Set 操作的时候都是被锁包裹起来的就行,因此,不需要并发安全的 Set 集合 。
即注释上写的:Accessed only under mainLock.
记住了,有可能会被考哦 。
然后,老爷子说的第二个原因:
这句话就是说的关于加锁好维护 largestPoolSize 这个参数,不再贅述了 。
哦,对了,这是有个 etc,表示“诸如此类”的意思 。
这个 etc 指的就是这个 completedTaskCount 参数,道理是一样的:
除了前面说的 mainLock 外,线程池里面其实还有一把经常被大家忽略的锁 。
那就是 Worker 对象 。
可以看到 Worker 是继承自 AQS 对象的,它的很多方法也是和锁相关的 。
同时它也实现了 Runnable 方法,所以说到底它就是一个被封装起来的线程,用来运行提交到线程池里面的任务,当没有任务的时候就去队列里面 take 或者 poll 等着,命不好的就被回收了 。
推荐阅读
- 高筋面粉、中筋面粉、低筋面粉 面粉是什么做的
- 马斯克哀叹德国官僚作风:柏林工厂投产或再次延期
- css font-style属性的作用是什么
- 外部css样式表的作用是什么
- css的基本选择器有哪些
- 年年有余的鱼寓意是什么,吃鱼是年年有余的意思找其他相似的语言
- 广州三个值得一去的摩天轮 广州摩天轮
- 手机4G信号满格,上网速度却很慢 手机网速慢的原因
- 感恩节祝福语简短句子大全感谢祝福父母老师的句子
