面试官q :我在说线程状态的WAITING状态,什么时候变成这个状态,什么时候离开这个状态呢
小菜j笑…是 ...
无限期等待其他线程执行特殊操作的线程处于WAITING状态。
athrethathasistintectinginfinityforadatheretoperappraticationisinthestate .
但是,这里并不详细说明什么是“特殊动作”,是否详细定义javadoc(jdk8)
线程进入WAITING状态是因为调用了以下方法
无限期的Object.wait方法 无时限的Thread.join方法 LockSupport.park 等待其他线程采取下列特殊行动:
调用某个对象的Object.wait方法的线程等待另一个线程调用该对象的Object.notify ( )或Object.notifyAll ( )。 调用Thread.join方法的线程等待指定线程的终止。 对应的英文原文如下:
athrethisinthestinthetingstateduetocallingoneofthefollowingsmethods :
Object.wait with no timeout
Thread.join with no timeout .
LockSupport.park
athresinthestinthetingstatitingforadtherestoperforapprateaparticularaction.for example athreadthathascalledobject.wait ( ) 请参见onnanobjectistingforanotherthreadtoccallobject.notify ( ) or object.notify all ( ),that object.athrethathashecalleddthread.join
线程之间的“协作”( cooperate )机制
很明显,关于WAITING状态,不是一个线程的单独游戏,相反,是多个线程,具体来说是多个线程之间的协调机构。 对于线程,我们经常考虑线程之间的竞争( race )。 例如,互相争夺锁,这不是故事的全部,线程之间也有协作机构。
在公司里你和你的同事们,可能会有晋升时的竞争,但更多的时候你们会合作完成工作。
wait/notify是线程之间的协作机制,为什么首先是wait? 什么时候? 为什么其他线程会等待执行“特殊操作”? 解决了什么样的问题?
wait场景
首先,简单地说,为什么需要等待,是因为不满足条件。 那么什么是条件呢? 为了便于理解,假设以下场景:
有火车车厢,有很多乘客,乘客有相当于线程的厕所。 这是公共资源,一次只能访问一个线程(最终,没有人想在上厕所期间与其他人共享)。
竞争关系
如果多名乘客想同时上厕所,这里首先有竞争的关系。
以厕所为对象的话,因为有钥匙,想去厕所的乘客的线程必须先拿好钥匙再进厕所。
Java在语言级别提供同步机制,即synchronized关键字
已同步( expression ) {……}
该机制可以计算“表达式”( expresssion ) (值类型必须为“引用类型”( reference type ) ),并尝试检索表达式表示的对象并检索对象的锁定
如果能够取得锁,则进入同步块的执行,执行后结束同步块,返回对象的锁(即使异常结束也返回)。 如果无法获取锁,请在此阻止它,直到可以获取锁。 当某个线程在厕所时,想要去其他厕所的线程被阻止,位于该厕所对象的entry set中,处于BLOCKED状态。
完了,离开厕所,把钥匙还给你。
然后,系统在entry set中选择线程并将其锁定。
在上述步骤中,下图显示了gif动画
当然,这是我们熟悉的摇滚竞争过程。 下面是演示的代码
条件
现在女乘客抓住钥匙进去后,裤子脱了一半,发现厕所的衬垫纸不见了,便拒绝了尿。
因为她很卫生,直接坐下可能会弄脏白花屁股
现在条件出来了。 有纸而没有纸的条件。
那么,现在的条件还没有得到满足,这个女线程该怎么办呢?只是在中间等着,显然是不行的。
这不就是人民群众深恶痛绝的“占厕不吸尿”吗?
另一方面,在外部entry set,很多人可能还在等待尿(其中可能有很多主线程,他们不在乎厕所里有没有垫纸)。 另一方面,假设外面有“乘务员线程”,进去想增加密封纸,但是里面不能占有,人也不能进去,纸也不能进去。 因此,在不满足条件时,需要返还钥匙,像“乘务员线程”那样增加用纸。
我需要等吗?
那么,出来的话一定要等吗?当然不一定。
这里的“待机”是指使线程处于不活动的状态,即从调度队列中排除。
如果不等待,则只需返回锁定并重复确定条件是否满足,然后再次返回调度队列,预计下次调度时条件可能会发生变化
例如,某“乘务员线程”以前已经安排好,里面的垫片纸也被追加了。 当然,可能会重新安排,但条件尚未满足。
厕所外面进入很多“女乘客线程”变得方便的同时,也进入焦躁不安的“乘务员线程”,考虑一下想要增加卫生纸的极端状况吧。
线程不等的话,厕所是公共资源,不能同时访问。 调度程序每次选择线程时选择“乘务员线程”的概率反而变低,在entry set中,越聚集越方便的“女乘客线程”无法实现的可能性高,选择“乘务员线程”的概率越来越低。
当然,同步机构防止了所谓的“starvation”现象的发生,“乘务员线程”最终有机会执行,只是降低了系统的运行效率。
因此,会妨碍正常运行的线程,缩减资源,反而会影响自己条件的满足。 另外,“乘务员线程”可能还没有开始,这个时候,不想等的“女乘客线程”白白进入,占用CPU资源也没有成功。
有效地,在这种无进展的出入口中等待,类似于所谓的繁忙等待。
合作关系
由此可见,等待是必要的,更有效率的机制,即wait/notify协作机制是必要的。
当不满足条件时应该调用wait ( )方法,线程解除锁定,进入所谓wait set,具体而言进入作为此厕所对象的wait set :
0
此时,线程不再活动,不再参加计划,因此不会浪费CPU资源,也不会去竞争锁,此时的线程状态为WAITING。
现在的问题是,她们什么时候能够再次活动,当然,最佳时机是满足条件的时候。
之后,进入“乘务员线程”,增加了卫生纸,当然,在这个时候仅仅放入卫生纸是不能完成的,特别的动作,也就是“通知( notify )”执行了等待这个对象的女性的乘客线程
“我有一张纸! 快去尿吧! ’很明显,只要“女乘客线程”一方愿意等待,她们就没有机会再执行了。
“通知”是指将她们从wait set中解放出来,再次放入调度队列( ready queue )。
如果为notify,请选择释放被通知方的wait set 对于通知全部,释放被通知的wait set上的所有线程。 整个过程如下图所示
上述步骤还包括以下gif视频演示
注:如果只通知一个正在等待的线程,则通知的线程无法立即恢复运行。 由于她中断的地方是同步块,现在没有锁,必须重新检索锁(很可能面对其他线程冲突),并且只有在成功后,wait方法才能在调用时重新启动执行。 (这是所谓的“reenter after calling Object.wait”)
如果能够获取锁,线程将从WAITING状态变为RUNNABLE状态 否则,从wait set出发进入entry set,线程从WAITING状态变为BLOCKED状态。 如上所述,这是协调机构,“女乘客线程”和“乘务员线程”之间存在协调关系。 显然,这种合作关系的存在,“女乘客线程”可以避免在条件不满足时的盲目尝试,在释放资源以便“乘务员线程”顺利运行的同时,也可以在条件满足时立即收到通知。 合作关系的存在可以互相利益。
生产者和消费者问题
显然,以上本质是典型的“生产者和消费者”问题
乘务员线程生产卫生纸,女乘客线程消费卫生纸。 没有卫生纸的情况下(不满足条件),女性乘客线程在等待,乘务员线程添加卫生纸(满足条件),通知女性乘客线程(解除她们的待机状态)。 其次,女乘客的线程能否进一步执行取决于锁的获得情况。
代码演示:
下面的代码显示了上述wait/notify过程
join场景等
由定义可知,除wait/notify外,调用join方法时线程变为WAITING状态。
join的机构中没有显式的wait/notify的调用,但可以看作是特殊的隐式wait/notify机构。
如果有a、b两个线程,在a线程中执行b.join ( ),则a等待b,此时a停止执行,将执行b这一情况在系统内部默认通知a,a解除待机状态,重新开始执行。
即,a等待的条件为“b执行完成”,b完成时自动通知给a。
读者自己分析LockSupport.park。
与传统waiting状态的关系
Thread.State.WAITING状态类似于传统的waiting状态
我现在在职的Java开发,如果你现在知道Java技术,想学好Java,想成为Java开发技术人员,在入门学习Java的过程中没有基础的入门视频教程,你可以关注我,私信: 01。 这里有最新的Java基础JavaSE讲义视频教程。 这个视频教程是我今年初根据市场技术堆栈的需求录制的,是一个非常完整的系统。