实现需求:
开启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的条件,并在条件实际上并未改变的情况下处理唤醒通知。