网站设计 字体,东城网站开发,沈阳工伤保险做实在哪个网站,网站建设与搜索看到一篇关于写线程waiting状态的文章#xff0c;感觉很生动有趣#xff0c;转过来保存下。
总结#xff1a;
waiting这个状态#xff0c;就是等待#xff0c;明确了等待#xff0c;就不会抢资源了。
一个线程A在拿到锁但不满足执行条件的时候#xff0c;需要另一个线…看到一篇关于写线程waiting状态的文章感觉很生动有趣转过来保存下。
总结
waiting这个状态就是等待明确了等待就不会抢资源了。
一个线程A在拿到锁但不满足执行条件的时候需要另一个线程B去满足这个条件 那么线程A就会释放锁并处于waiting的状态等线程B执行完再执行。 waiting状态的好处是此状态的线程不再活动不再参与调度因此不会浪费 CPU 资源也不会去竞争锁了相比暴力的blocking状态要优雅很多。
进入waiting的方法
调用wait方法就能让线程进入waiting状态notify/notify能让线程结束waiting状态。 join也能让线程进入waiting状态但也算是特殊的wait
还有个Time_waiting的状态
就是wait方法加了个时间的入参代表等多久就不等了避免永远等下去。
blocking和waiting
blocking状态硬说也算waiting的特殊情况 如果waiting状态的条件是有没有锁那就可以勉强理解为blocking状态吧。 blocking是线程被动阻塞waiting是线程主动阻塞本质上其实是一样的。
下文这个火车上抢厕所的例子很形象。
以下是转载正文末尾附原文链接 定义 一个正在无限期等待另一个线程执行一个特别的动作的线程处于这一状态。 A thread that is waiting indefinitely for another thread to perform a particular action is in this state. 然而这里并没有详细说明这个“特别的动作”到底是什么详细定义还是看 javadocjdk8 一个线程进入 WAITING 状态是因为调用了以下方法 不带时限的 Object.wait 方法不带时限的 Thread.join 方法LockSupport.park 然后会等其它线程执行一个特别的动作比如 一个调用了某个对象的 Object.wait 方法的线程会等待另一个线程调用此对象的 Object.notify() 或 Object.notifyAll()。一个调用了 Thread.join 方法的线程会等待指定的线程结束。 对应的英文原文如下 A thread is in the waiting state due to calling one of the following methods: Object.wait with no timeoutThread.join with no timeoutLockSupport.park A thread in the waiting state is waiting for another thread to perform a particular action. For example, a thread that has called Object.wait() on an object is waiting for another thread to call Object.notify() or Object.notifyAll() on that object. A thread that has called Thread.join() is waiting for a specified thread to terminate. 线程间的协作cooperate机制 显然WAITING 状态所涉及的不是一个线程的独角戏相反它涉及多个线程具体地讲这是多个线程间的一种协作机制。谈到线程我们经常想到的是线程间的竞争race比如去争夺锁但这并不是故事的全部线程间也会有协作机制。 就好比在公司里你和你的同事们你们可能存在在晋升时的竞争但更多时候你们更多是一起合作以完成某些任务。 wait/notify 就是线程间的一种协作机制那么首先为什么 wait什么时候 wait它为什么要等其它线程执行“特别的动作”它到底解决了什么问题 wait 的场景 首先为什么要 wait 呢简单讲是因为条件condition不满足。那么什么是条件呢为方便理解我们设想一个场景 有一节列车车厢有很多乘客每个乘客相当于一个线程里面有个厕所这是一个公共资源且一次只允许一个线程进去访问毕竟没人希望在上厕所期间还与他人共享~。 竞争关系 假如有多个乘客想同时上厕所那么这里首先存在的是竞争的关系。 如果将厕所视为一个对象它有一把锁想上厕所的乘客线程需要先获取到锁然后才能进入厕所。 Java 在语言级直接提供了同步的机制也即是 synchronized 关键字 synchronized(expression) {……} 它的机制是这样的对表达式expresssion求值值的类型须是引用类型reference type获取它所代表的对象然后尝试获取这个对象的锁 如果能获取锁则进入同步块执行执行完后退出同步块并归还对象的锁异常退出也会归还如果不能获取锁则阻塞在这里直到能够获取锁。 在一个线程还在厕所期间其它同时想上厕所的线程被阻塞处在该厕所对象的 entry set 中处于 BLOCKED 状态。 完事之后退出厕所归还锁。 之后系统再在 entry set 中挑选一个线程将锁给到它。 对于以上过程以下为一个 gif 动图演示 当然这就是我们所熟悉的锁的竞争过程。以下为演示的代码 Test
public void testBlockedState() throws Exception {class Toilet { // 厕所类public void pee() { // 尿尿方法try {Thread.sleep(21000);// 研究表明动物无论大小尿尿时间都在21秒左右} catch (InterruptedException e) {Thread.currentThread().interrupt();}}}
Toilet toilet span classhljs-keywordnew/span Toilet();Thread passenger1 span classhljs-keywordnew/span Thread(span classhljs-keywordnew/span Runnable() {span classhljs-functionspan classhljs-keywordpublic/span span classhljs-keywordvoid/span span classhljs-titlerun/spanspan classhljs-params()/span /span{span classhljs-keywordsynchronized/span (toilet) {toilet.pee();}}
});Thread passenger2 span classhljs-keywordnew/span Thread(span classhljs-keywordnew/span Runnable() {span classhljs-functionspan classhljs-keywordpublic/span span classhljs-keywordvoid/span span classhljs-titlerun/spanspan classhljs-params()/span /span{span classhljs-keywordsynchronized/span (toilet) {toilet.pee();}}
});passenger1.start();span classhljs-comment// 确保乘客1先启动/span
Thread.sleep(span classhljs-number100/span);passenger2.start();span classhljs-comment// 确保已经执行了 run 方法/span
Thread.sleep(span classhljs-number100/span);span classhljs-comment// 在乘客1在厕所期间乘客2处于 BLOCKED 状态/span
assertThat(passenger2.getState()).isEqualTo(Thread.State.BLOCKED);}
条件
现在假设有个女乘客她抢到了锁进去之后裤子脱了一半发现马桶的垫圈纸没了于是拒绝尿。 或许是因为她比较讲究卫生怕直接坐上去会弄脏她白花花的屁股~ 现在条件出现了有纸没纸这就是某种条件。 那么现在条件不满足这位女线程改怎么办呢如果只是在里面干等显然是不行的。 这不就是人民群众所深恶痛绝的“占着茅坑不拉尿”吗 一方面外面 entry set 中可能好多群众还嗷嗷待尿呢其中可能有很多大老爷线程他们才不在乎有没有马桶垫圈纸~另一方面假定外面同时有“乘务员线程”准备进去增加垫圈纸可你在里面霸占着不出来别人也没法进去也就没法加纸。
所以当条件不满足时需要出来要把锁还回去以使得诸如“乘务员线程”的能进去增加纸张。 等待是必要的吗
那么出来之后是否一定需要等待呢当然也未必。 这里所谓“等待”指的是使线程处于不再活动的状态即是从调度队列中剔除。 如果不等待只是简单归还锁用一个反复的循环来判断条件是否满足那么还是可以再次回到调度队列然后期待在下一次被调度到的时候可能条件已经发生变化 比如某个“乘务员线程”已经在之前被调度并增加了里面的垫圈纸。自然也可能再次调度到的时候条件依旧是不满足的。 现在让我们考虑一种比较极端的情况厕所外一大堆的“女乘客线程”想进去方便同时还有一个焦急的“乘务员线程”想进去增加厕纸。 如果线程都不等待而厕所又是一个公共资源无法并发访问。调度器每次挑一个线程进去挑中“乘务员线程”的几率反而降低了entry set 中很可能越聚越多无法完成方便的“女乘客线程”“乘务员线程”被选中执行的几率越发下降。 当然同步机制会防止产生所谓的“饥饿starvation”现象“乘务员线程”最终还是有机会执行的只是系统运行的效率下降了。 所以这会干扰正常工作的线程挤占了资源反而影响了自身条件的满足。另外“乘务员线程”可能这段时间根本没有启动此时不愿等待的“女乘客线程”不过是徒劳地进进出出占用了 CPU 资源却没有办成正事。 效果上还是在这种没有进展的进进出出中等待这种情形类似于所谓的忙等待 busy waiting。 协作关系
综上等待还是有必要的我们需要一种更高效的机制也即是 wait/notify 的协作机制。
当条件不满足时应该调用 wait()方法这时线程释放锁并进入所谓的 wait set 中具体的讲是进入这个厕所对象的 wait set 中 这时线程不再活动不再参与调度因此不会浪费 CPU 资源也不会去竞争锁了这时的线程状态即是 WAITING。 现在的问题是她们什么时候才能再次活动呢显然最佳的时机是当条件满足的时候。 之后“乘务员线程”进去增加厕纸当然此时它也不能只是简单加完厕纸就完了它还要执行一个特别的动作也即是“通知notify”在这个对象上等待的女乘客线程 大概就是向她们喊一声“有纸啦赶紧去尿吧”显然如果只是“女乘客线程”方面一厢情愿地等待她们将没有机会再执行。 所谓“通知”也即是把她们从 wait set 中释放出来重新进入到调度队列ready queue中。
如果是 notify则选取所通知对象的 wait set 中的一个线程释放如果是 notifyAll则释放所通知对象的 wait set 上的全部线程。
整个过程如下图所示 对于上述过程我们也给出以下 gif 动图演示 注意哪怕只通知了一个等待的线程被通知线程也不能立即恢复执行因为她当初中断的地方是在同步块内而此刻她已经不持有锁所以她需要再次尝试去获取锁很可能面临其它线程的竞争成功后才能在当初调用 wait 方法之后的地方恢复执行。这也即是所谓的 “reenter after calling Object.wait”在上一个篇章中也曾详细的讨论了这一过程。
如果能获取锁线程就从 WAITING 状态变成 RUNNABLE 状态否则从 wait set 出来又进入 entry set线程就从 WAITING 状态又变成 BLOCKED 状态。
综上这是一个协作机制“女乘客线程”和“乘务员线程”间存在一个协作关系。显然这种协作关系的存在“女乘客线程”可以避免在条件不满足时的盲目尝试也为“乘务员线程”的顺利执行腾出了资源同时在条件满足时又能及时得到通知。协作关系的存在使得彼此都能受益。 生产者与消费者问题
不难发现以上实质上也就是经典的“生产者与消费者”的问题 乘务员线程生产厕纸女乘客线程消费厕纸。当厕纸没有时条件不满足女乘客线程等待乘务员线程添加厕纸使条件满足并通知女乘客线程解除她们的等待状态。接下来女乘客线程能否进一步执行则取决于锁的获取情况。 代码的演示
在以下代码中演示了上述的 wait/notify 的过程
Test
public void testWaitingState() throws Exception {
span classhljs-classspan classhljs-keywordclass/span span classhljs-titleToilet/span /span{ span classhljs-comment// 厕所类/spanspan classhljs-keywordint/span paperCount span classhljs-number0/span; span classhljs-comment// 纸张/spanspan classhljs-functionspan classhljs-keywordpublic/span span classhljs-keywordvoid/span span classhljs-titlepee/spanspan classhljs-params()/span /span{ span classhljs-comment// 尿尿方法/spanspan classhljs-keywordtry/span {Thread.sleep(span classhljs-number21000/span);span classhljs-comment// 研究表明动物无论大小尿尿时间都在21秒左右/span} span classhljs-keywordcatch/span (InterruptedException e) {Thread.currentThread().interrupt();}}
}Toilet toilet span classhljs-keywordnew/span Toilet();span classhljs-comment// 两乘客线程/span
Thread[] passengers span classhljs-keywordnew/span Thread[span classhljs-number2/span];
span classhljs-keywordfor/span (span classhljs-keywordint/span i span classhljs-number0/span; i lt; passengers.length; i) {passengers[i] span classhljs-keywordnew/span Thread(span classhljs-keywordnew/span Runnable() {span classhljs-functionspan classhljs-keywordpublic/span span classhljs-keywordvoid/span span classhljs-titlerun/spanspan classhljs-params()/span /span{span classhljs-keywordsynchronized/span (toilet) {span classhljs-keywordwhile/span (toilet.paperCount lt; span classhljs-number1/span) {span classhljs-keywordtry/span {toilet.wait(); span classhljs-comment// 条件不满足等待/span} span classhljs-keywordcatch/span (InterruptedException e) {Thread.currentThread().interrupt();}}toilet.paperCount--; span classhljs-comment// 使用一张纸/spantoilet.pee();}}});
}span classhljs-comment// 乘务员线程/span
Thread steward span classhljs-keywordnew/span Thread(span classhljs-keywordnew/span Runnable() {span classhljs-functionspan classhljs-keywordpublic/span span classhljs-keywordvoid/span span classhljs-titlerun/spanspan classhljs-params()/span /span{span classhljs-keywordsynchronized/span (toilet) {toilet.paperCount span classhljs-number10/span;span classhljs-comment// 增加十张纸/spantoilet.notifyAll();span classhljs-comment// 通知所有在此对象上等待的线程/span}}
});passengers[span classhljs-number0/span].start();
passengers[span classhljs-number1/span].start();span classhljs-comment// 确保已经执行了 run 方法/span
Thread.sleep(span classhljs-number100/span);span classhljs-comment// 没有纸两线程均进入等待状态/span
assertThat(passengers[span classhljs-number0/span].getState()).isEqualTo(Thread.State.WAITING);
assertThat(passengers[span classhljs-number1/span].getState()).isEqualTo(Thread.State.WAITING);span classhljs-comment// 乘务员线程启动救星来了/span
steward.start();span classhljs-comment// 确保已经增加纸张并已通知/span
Thread.sleep(span classhljs-number100/span);span classhljs-comment// 其中之一会得到锁并执行 pee但无法确定是哪个所以用 或 ||/span
span classhljs-comment// 注因为 pee 方法中实际调用是 sleep 所以很快就从 RUNNABLE 转入 TIMED_WAITING(sleep 时对应的状态)/span
assertTrue(Thread.State.TIMED_WAITING.equals(passengers[span classhljs-number0/span].getState())|| Thread.State.TIMED_WAITING.equals(passengers[span classhljs-number1/span].getState()));span classhljs-comment// 其中之一则被阻塞但无法确定是哪个所以用 或 ||/span
assertTrue(Thread.State.BLOCKED.equals(passengers[span classhljs-number0/span].getState()) || Thread.State.BLOCKED.equals(passengers[span classhljs-number1/span].getState()));}
join 的场景及其它
从定义中可知除了 wait/notify 外调用 join 方法也会让线程处于 WAITING 状态。
join 的机制中并没有显式的 wait/notify 的调用但可以视作是一种特殊的隐式的 wait/notify 机制。
假如有 ab 两个线程在 a 线程中执行 b.join()相当于让 a 去等待 b此时 a 停止执行等 b 执行完了系统内部会隐式地通知 a使 a 解除等待状态恢复执行。 换言之a 等待的条件是 “b 执行完毕”b 完成后系统会自动通知 a。 关于 LockSupport.park 的情况则由读者自行分析。 与传统 waiting 状态的关系
Thread.State.WAITING 状态与传统的 waiting 状态类似 本文后面部分转载自https://my.oschina.net/goldenshaw/blog/802620
如有侵权请告知删除