添加任务的规律
先核心,再任务队列,再非核心,再饱和策略
有哪些饱和策略
(default)throw exception
run
{}
queue.poll,再次execute
正确关闭
(具体可以看https://blog.csdn.net/zaozi/article/details/38854561)
最正确的还是通过打标记位,不过interrupt是系统默认替我们实现的标记位。我们只需要在线程运行的时候,时不时去检测这个标记位即可。然后future的关闭其实也是一样的,和runnable没啥区别。
梳理下比较重要的源码流程
从execute入手,第一时间看见的就是我们的核心规则:先核心线程,再队列,再非核心线程,再饱和策略。其中核心线程和非核心线程的增加是通过addWorker方法来的,首先会有一个双重for循环配合cas与retry实现worker count+1。然后会加悲观锁,创建worker,创建成功后,启动线程。然后会执行worker的runWorker方法,首先会执行我们第一个加进去的任务,然后会进入无限循环,条件是getTask,如果队列中没有任务了,会阻塞在取元素的方法上。如果有新任务到来,会唤醒取元素的方法。这就是线程复用的奥秘。
为什么Worker要继承AQS?他是怎么应用AQS的特性的?
其实worker已经几乎无异于一个独占锁了。提供了lock与unlock方法,和reentrantlock极其地相似。他不期望worker的线程在执行任务的时候,外部线程来中断任务。因此worker在执行任务之前,会worker.lock,在执行任务之后,会worker.unlock。同时在interrupt方法中,在中断每个worker之前都会worker.trylock,在调用thread.interrupt方法后,会worker.unlock。这样可以实现比如在中断一个worker的过程中,任务执行处阻塞在worker.lock处,在中断一个worker后,执行worker.unlock,因此任务执行处被唤醒,得以继续执行。而任务在lock的时候,trylock会返回false,直接不中断这个worker了。
这样就实现了interrupt idle workers的功能。
但是为什么一定要让Worker继承AQS来实现清理空闲任务的功能呢?有没有另外的更好的实现?
理一下思路,如果我们要实现这个功能
1.检测他是空闲的
2.然后置为正在中断中,避免他进入工作状态
显然需要用cas
我们在thread.interrupt后,希望让他继续工作状态
那么显然lock是最好的,他要进入工作状态的时候,检测到正在中断中,阻塞;然后unlock一下,让他继续执行
同时他在执行的时候,我需要检测他的工作状态,然后尝试置为正在中断中
显然也要cas
然后如果失败,就不中断这个执行任务中的线程了。
任务执行完后,unlock,这样才有机会trylock成功。
再问,用reentrantlock代替行不行?
那么我中断的时候,就不再是trylock了,而是lock,这也意味着我在尝试中断的时候,检测到线程正在运行,那么我会阻塞,而不是跳过。显然reentrantlock实现不了这个操作,而且他也不开放tryLock这个api。
//说错了,是中断,而不是清理(必须能够响应中断才能在中断的时候被清理)