实现需求:

开启2个线程,1个线程对某个int类型成员变量加1,另外1个减1,但是要次序执行,即如果int型的成员变量是0,则输出01010101这样的结果

代码如下

 1 package test;
2
3 public class Sample {
4
5 private int i;
6
7 public synchronized void increase() {
8
9 if(i != 0) { // 值不为0时,已经加过,释放锁,等待其他线程减为0
10 try {
11 wait();
12 } catch (Exception e) {
13 e.printStackTrace();
14 }
15 }
16
17 // 值为0,需要增加
18 i++;
19 System.out.println(i);
20 // 通知其他线程,可以进行减1的操作了
21 notify();
22 }
23
24 public synchronized void decrease() {
25 if(i == 0) { // 值为0时,已经减过,释放锁,等待其他线程加为1
26 try {
27 wait();
28 } catch (Exception e) {
29 e.printStackTrace();
30 }
31 }
32
33 // 值为1,需要减少
34 i--;
35 System.out.println(i);
36 // 通知其他线程,可以进行加1的操作了
37 notify();
38 }
39
40 public static void main(String[] args) {
41
42 Sample sample = new Sample();
43
44 Thread t1 = new IncreaseThread(sample);
45 Thread t2 = new DecreaseThread(sample);
46
47 t1.start();
48 t2.start();
49 }
50 }
51
52 class DecreaseThread extends Thread {
53
54 private Sample sample;
55
56 public DecreaseThread(Sample sample) {
57 this.sample = sample;
58 }
59
60 @Override
61 public void run() {
62 for (int i = 0; i < 20; i++) {
63 try {
64 Thread.sleep((long) Math.random() * 1000);
65 } catch (Exception e) {
66 e.printStackTrace();
67 }
68
69 sample.decrease();
70 }
71 }
72 }
73
74 class IncreaseThread extends Thread {
75
76 private Sample sample;
77
78 public IncreaseThread(Sample sample) {
79 this.sample = sample;
80 }
81
82 @Override
83 public void run() {
84 for (int i = 0; i < 20; i++) {
85 try {
86 Thread.sleep((long) Math.random() * 1000);
87 } catch (Exception e) {
88 e.printStackTrace();
89 }
90
91 sample.increase();
92 }
93 }
94 }

需求稍作改变,变成:

开启4个线程,2个线程对某个int类型成员变量加1,另外2个减1,但是要次序执行,即如果int型的成员变量是0,则输出01010101这样的结果

如果是直接再生成t3 t4分别是IncreaseThread和DecreaseThread的实例(即t1/t3为IncreaseThread类的实例,t2/t4为DecreaseThread的实例),假设执行流程如下:

(1)t2执行,由于i=0,所以线程等待,释放锁,随机通知一条线程进行执行(notify()方法是通知随机一条线程的)

(2)假设通知到了t4,由于i=0,所以t4线程又等待,锁释放,又随机通知到一条线程进行执行

(3)假设又通知到了t2线程,这个时候,线程的执行是从wait()方法后面开始执行的,不会再去判断i是否等于0了,继续执行,会进行i的自减操作,出现i=-1的局面

所以代码需要修改

 1 package test;
2
3 public class Sample {
4
5 private int i;
6
7 public synchronized void increase() {
8
9 while(i != 0) { // 值不为0时,已经加过,释放锁,等待其他线程减为0
10 try {
11 wait();
12 } catch (Exception e) {
13 e.printStackTrace();
14 }
15 }
16
17 // 值为0,需要增加
18 i++;
19 System.out.println(i);
20 // 通知其他线程,可以进行减1的操作了
21 notify();
22 }
23
24 public synchronized void decrease() {
25 while(i == 0) { // 值为0时,已经减过,释放锁,等待其他线程加为1
26 try {
27 wait();
28 } catch (Exception e) {
29 e.printStackTrace();
30 }
31 }
32
33 // 值为1,需要减少
34 i--;
35 System.out.println(i);
36 // 通知其他线程,可以进行加1的操作了
37 notify();
38 }
39
40 public static void main(String[] args) {
41
42 Sample sample = new Sample();
43
44 Thread t1 = new IncreaseThread(sample);
45 Thread t2 = new DecreaseThread(sample);
46 Thread t3 = new IncreaseThread(sample);
47 Thread t4 = new DecreaseThread(sample);
48
49 t1.start();
50 t2.start();
51 t3.start();
52 t4.start();
53 }
54 }
55
56 class DecreaseThread extends Thread {
57
58 private Sample sample;
59
60 public DecreaseThread(Sample sample) {
61 this.sample = sample;
62 }
63
64 @Override
65 public void run() {
66 for (int i = 0; i < 20; i++) {
67 try {
68 Thread.sleep((long) Math.random() * 1000);
69 } catch (Exception e) {
70 e.printStackTrace();
71 }
72
73 sample.decrease();
74 }
75 }
76 }
77
78 class IncreaseThread extends Thread {
79
80 private Sample sample;
81
82 public IncreaseThread(Sample sample) {
83 this.sample = sample;
84 }
85
86 @Override
87 public void run() {
88 for (int i = 0; i < 20; i++) {
89 try {
90 Thread.sleep((long) Math.random() * 1000);
91 } catch (Exception e) {
92 e.printStackTrace();
93 }
94
95 sample.increase();
96 }
97 }
98 }

这里把if判断改成了while循环,因为wait方法之后,应该是需要重复判断一次i的情况的,这样就不会出现数字不对的情况了

这里有一条基本原则:

永远在while循环里而不是if语句下使用wait。这样,会在线程暂停恢复后都检查wait的条件,并在条件实际上并未改变的情况下处理唤醒通知

最新文章

  1. LINQ系列:LINQ to XML操作
  2. 设计一个较好的框架的难点之一--API兼容性的设计
  3. 当struts遇上json,没爱了
  4. 颜色渐变的JS代码
  5. python_day3
  6. bug 调试了一个下午外加半个晚上的bug
  7. 编译Debian内核源码
  8. BUG修改纪录
  9. [转] GCC __builtin_expect的作用
  10. QT序列化操作(比较复杂和完善)
  11. C# CacheHelper
  12. C#之out修饰符、ref修饰符、params修饰符的简单介绍
  13. 【NOIP2015提高组】Day2 T1 跳石头
  14. maven项目与普通项目的区别
  15. Android之获取屏幕的尺寸像素及获取状态栏标题栏高度
  16. python 所有常用模块汇总
  17. vue加载优化策略
  18. ini (ini-parser)配置文件解析 for donet
  19. C#.NET常见问题(FAQ)-构造器constructor有什么用
  20. tornado 的 define 和options方法解读

热门文章

  1. day3(使用axios实现登录成功)
  2. 老猿学5G专栏文章目录
  3. 第三十七章、PyQt输入部件:QAbstractSlider派生类QScrollBar滚动条、QSlider滑动条、QDial刻度盘功能介绍
  4. 第8.32节 Python中重写__delattr__方法捕获属性删除
  5. 关于将Linux中默认的OpenJDK替换为JDK的方法
  6. Hello!OA!Hello!工作流!寻找OA和工作流的旅途记录
  7. vue之keep-alive组件
  8. 题解-Words
  9. IDM(Internet Download Manager)—下载各类安装包(github代码、python包)、软件、视频、文档的神器,居家必备良药
  10. 1.pipeline原理