Vivian
2018-07-03
来源 :
阅读 962
评论 0
摘要:本文主要向大家介绍了【JAVA语言并发编程】之十一:线程间通信中notify通知的遗漏(含代码),通过具体的内容向大家展示,希望对大家学习JAVA语言有所帮助。
本文主要向大家介绍了【JAVA语言并发编程】之十一:线程间通信中notify通知的遗漏(含代码),通过具体的内容向大家展示,希望对大家学习JAVA语言有所帮助。
notify通知的遗漏很容易理解,即threadA还没开始wait的时候,threadB已经notify了,这样,threadB通知是没有任何响应的,当threadB退出synchronized代码块后,threadA再开始wait,便会一直阻塞等待,直到被别的线程打断。
遗漏通知的代码
下面给出一段代码演示通知是如何遗漏的,如下:
[java] view plain copy
1. public class MissedNotify extends Object {
2. private Object proceedLock;
3.
4. public MissedNotify() {
5. print("in MissedNotify()");
6. proceedLock = new Object();
7. }
8.
9. public void waitToProceed() throws InterruptedException {
10. print("in waitToProceed() - entered");
11.
12. synchronized ( proceedLock ) {
13. print("in waitToProceed() - about to wait()");
14. proceedLock.wait();
15. print("in waitToProceed() - back from wait()");
16. }
17.
18. print("in waitToProceed() - leaving");
19. }
20.
21. public void proceed() {
22. print("in proceed() - entered");
23.
24. synchronized ( proceedLock ) {
25. print("in proceed() - about to notifyAll()");
26. proceedLock.notifyAll();
27. print("in proceed() - back from notifyAll()");
28. }
29.
30. print("in proceed() - leaving");
31. }
32.
33. private static void print(String msg) {
34. String name = Thread.currentThread().getName();
35. System.out.println(name + ": " + msg);
36. }
37.
38. public static void main(String[] args) {
39. final MissedNotify mn = new MissedNotify();
40.
41. Runnable runA = new Runnable() {
42. public void run() {
43. try {
44. //休眠1000ms,大于runB中的500ms,
45. //是为了后调用waitToProceed,从而先notifyAll,后wait,
46. //从而造成通知的遗漏
47. Thread.sleep(1000);
48. mn.waitToProceed();
49. } catch ( InterruptedException x ) {
50. x.printStackTrace();
51. }
52. }
53. };
54.
55. Thread threadA = new Thread(runA, "threadA");
56. threadA.start();
57.
58. Runnable runB = new Runnable() {
59. public void run() {
60. try {
61. //休眠500ms,小于runA中的1000ms,
62. //是为了先调用proceed,从而先notifyAll,后wait,
63. //从而造成通知的遗漏
64. Thread.sleep(500);
65. mn.proceed();
66. } catch ( InterruptedException x ) {
67. x.printStackTrace();
68. }
69. }
70. };
71.
72. Thread threadB = new Thread(runB, "threadB");
73. threadB.start();
74.
75. try {
76. Thread.sleep(10000);
77. } catch ( InterruptedException x ) {}
78.
79. //试图打断wait阻塞
80. print("about to invoke interrupt() on threadA");
81. threadA.interrupt();
82. }
83. }
执行结果如下:

分析:由于threadB在执行mn.proceed()之前只休眠了500ms,而threadA在执行mn.waitToProceed()之前休眠了1000ms,因此,threadB会先苏醒,继而执行mn.proceed(),获取到proceedLock的对象锁,继而执行其中的notifyAll(),当退出proceed()方法中的synchronized代码块时,threadA才有机会获取proceedLock的对象锁,继而执行其中的wait()方法,但此时notifyAll()方法已经执行完毕,threadA便漏掉了threadB的通知,便会阻塞下去。后面主线程休眠10秒后,尝试中断threadA线程,使其抛出InterruptedException。
修正后的代码
为了修正MissedNotify,需要添加一个boolean指示变量,该变量只能在同步代码块内部访问和修改。修改后的代码如下:
[java] view plain copy
1. public class MissedNotifyFix extends Object {
2. private Object proceedLock;
3. //该标志位用来指示线程是否需要等待
4. private boolean okToProceed;
5.
6. public MissedNotifyFix() {
7. print("in MissedNotify()");
8. proceedLock = new Object();
9. //先设置为false
10. okToProceed = false;
11. }
12.
13. public void waitToProceed() throws InterruptedException {
14. print("in waitToProceed() - entered");
15.
16. synchronized ( proceedLock ) {
17. print("in waitToProceed() - entered sync block");
18. //while循环判断,这里不用if的原因是为了防止早期通知
19. while ( okToProceed == false ) {
20. print("in waitToProceed() - about to wait()");
21. proceedLock.wait();
22. print("in waitToProceed() - back from wait()");
23. }
24.
25. print("in waitToProceed() - leaving sync block");
26. }
27.
28. print("in waitToProceed() - leaving");
29. }
30.
31. public void proceed() {
32. print("in proceed() - entered");
33.
34. synchronized ( proceedLock ) {
35. print("in proceed() - entered sync block");
36. //通知之前,将其设置为true,这样即使出现通知遗漏的情况,也不会使线程在wait出阻塞
37. okToProceed = true;
38. print("in proceed() - changed okToProceed to true");
39. proceedLock.notifyAll();
40. print("in proceed() - just did notifyAll()");
41.
42. print("in proceed() - leaving sync block");
43. }
44.
45. print("in proceed() - leaving");
46. }
47.
48. private static void print(String msg) {
49. String name = Thread.currentThread().getName();
50. System.out.println(name + ": " + msg);
51. }
52.
53. public static void main(String[] args) {
54. final MissedNotifyFix mnf = new MissedNotifyFix();
55.
56. Runnable runA = new Runnable() {
57. public void run() {
58. try {
59. //休眠1000ms,大于runB中的500ms,
60. //是为了后调用waitToProceed,从而先notifyAll,后wait,
61. Thread.sleep(1000);
62. mnf.waitToProceed();
63. } catch ( InterruptedException x ) {
64. x.printStackTrace();
65. }
66. }
67. };
68.
69. Thread threadA = new Thread(runA, "threadA");
70. threadA.start();
71.
72. Runnable runB = new Runnable() {
73. public void run() {
74. try {
75. //休眠500ms,小于runA中的1000ms,
76. //是为了先调用proceed,从而先notifyAll,后wait,
77. Thread.sleep(500);
78. mnf.proceed();
79. } catch ( InterruptedException x ) {
80. x.printStackTrace();
81. }
82. }
83. };
84.
85. Thread threadB = new Thread(runB, "threadB");
86. threadB.start();
87.
88. try {
89. Thread.sleep(10000);
90. } catch ( InterruptedException x ) {}
91.
92. print("about to invoke interrupt() on threadA");
93. threadA.interrupt();
94. }
95. }
执行结果如下:

注意代码中加了注释的部分,在threadB进行通知之前,先将okToProceed置为true,这样如果threadA将通知遗漏,那么就不会进入while循环,也便不会执行wait方法,线程也就不会阻塞。如果通知没有被遗漏,wait方法返回后,okToProceed已经被置为true,下次while循环判断条件不成立,便会退出循环。
这样,通过标志位和wait、notifyAll的配合使用,便避免了通知遗漏而造成的阻塞问题。
总结:在使用线程的等待/通知机制时,一般都要配合一个boolean变量值(或者其他能够判断真假的条件),在notify之前改变该boolean变量的值,让wait返回后能够退出while循环(一般都要在wait方法外围加一层while循环,以防止早期通知),或在通知被遗漏后,不会被阻塞在wait方法处。这样便保证了程序的正确性。
本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注编程语言JAVA频道!
喜欢 | 2
不喜欢 | 0
您输入的评论内容中包含违禁敏感词
我知道了

请输入正确的手机号码
请输入正确的验证码
您今天的短信下发次数太多了,明天再试试吧!
我们会在第一时间安排职业规划师联系您!
您也可以联系我们的职业规划师咨询:
版权所有 职坐标-一站式AI+学习就业服务平台 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
沪公网安备 31011502005948号