问题描述
题目:两个线程操作一个变量,实现两个线程对同一个资源一个进行加1操作,另外一个进行减1操作,且需要交替实现,变量的初始值为0。即两个线程对同一个资源进行加一减一交替操作。话不多说,开干
首先我们先定义操作的资源,并且定义方法。
首先定义资源类
//资源类 class Resource { private int number = 0; public synchronized void up() throws InterruptedException { //1.判断 if(number != 0) { this.wait(); } //2.干活 number++; System.out.println(Thread.currentThread().getName() + " " + number); this.notifyAll(); } public synchronized void down() throws InterruptedException { if(number == 0) { this.wait(); } number--; System.out.println(Thread.currentThread().getName() + " " + number); this.notifyAll(); } }
接着我们写我们的两个线程
public class ThreadWaitNotifyDemo { public static void main(String[] args) { Resource resource = new Resource(); new Thread(() -> { for (int i = 0; i < 10; i++) { try { resource.up(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "A").start(); new Thread(() -> { for (int i = 0; i < 10; i++) { try { resource.down(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "B").start(); } }
结果如下图
这里我们通过Lambda表达式来通过匿名内部类来创建线程并启动,可以看到一个线程up方法,另外一个线程进行down方法,
首先比如线程A先判断是否为0,不为0,那么此时进行加1操作,同一时刻的B线程此时判断的是number等于0,那么就进行等待操作,这个时候A线程加完了,然后通过notifyAll()方法来唤醒其他的线程,所以就完成了减的操作,这里for0~10是为了保证能够交替进行10次。
wait 方法和notify方法是Thread的方法吗?
这里扩展一个知识点,wait 方法和notify方法是Thread的方法吗?
答案:错错错 。
查看API我们可以见到这两个方法属于Object的方法,wait 和 notify 必须要配合synchronized 关键字使用。
这就完了吗??
需求变更
不不不,此时需求改动了!这个时候项目经理过来说,小王,我不要两个线程操作了,我要四个线程同时操作,两个进行相加,两个进行相减,还是交替到时必须是0,1相互的交替。
这个时候心想,简单呀,我再多加两个线程!
于是,多加了两个线程。
代码如下,Resource不变。
public class ThreadWaitNotifyDemo { public static void main(String[] args) { Resource resource = new Resource(); new Thread(() -> { for (int i = 0; i < 10; i++) { try { resource.up(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "A").start(); new Thread(() -> { for (int i = 0; i < 10; i++) { try { resource.down(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "B").start(); new Thread(() -> { for (int i = 0; i < 10; i++) { try { resource.up(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "C").start(); new Thread(() -> { for (int i = 0; i < 10; i++) { try { resource.down(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "D").start(); } }
和之前的一样,多加了两个线程,一个进行加,一个进行减操作。
然后run一下,结果如下图:
这里我们就看到,有的为3了,这是什么情况???
分析:举个例子,A,C线程是进行加操作,B,D是进行减操作,此时如果是0,那么B,和D线程是被wait了的对吧,A和C因为up方法被加了锁,所以只有一个方法进行加,如果此时A进行完操作,然后再notifyall,那么此时C线程也会进行up操作,
因为C线程在if(number!=0){this.wait();}这里被唤醒后,继续进行其他的操作了,且在A执行完加1操作和后没有继续判断number的情况,同理B,D线程也是如此,所以就会出现上图的情况!
那么这种情况怎么解决呢??
解决方案
因为我们是没有重新进行判断,那么,我们让其重新进行判断就是了!
修改资源类代码如下:
class Resource { private int number = 0; public synchronized void up() throws InterruptedException { //1.判断 while (number != 0) { this.wait(); } //2.干活 number++; System.out.println(Thread.currentThread().getName() + " " + number); this.notifyAll(); } public synchronized void down() throws InterruptedException { while (number == 0) { this.wait(); } number--; System.out.println(Thread.currentThread().getName() + " " + number); this.notifyAll(); } }
四个线程的操作资源的代码不变,结果如下: