关于notify() 和notifyAll() 一个需要注意的地方
notify() 和 notifyAll()都是唤醒其他正在等待同一个对象锁的线程。
下面是我遇到的一个问题,记下来,免得忘了。
直接上代码,有错误的代码:
代码描述:有一个Caculate类,类中又一个成员变量 j,现在有多个线程对这个变量进行操作。一个增加操作、一个减少操作。增加操作:当 j = 0 时,j++ 。减少操作:当 j = 1 时,j-- 。这两个操作分别对应这 add()方法 和sub()方法,都使用synchronized关键字。
可以直接复制拿来运行一下
package com.zcd2; public class ThreadTest1 { public static void main(String[] args) { Caculate caculate = new Caculate(); //使用多个线程对实例caculate进行增加操作。 for(int i = 0; i < 10; i++) { Thread1 t = new Thread1(caculate); t.start(); } //使用多个线程对实例caculate进行减少操作。 for(int i = 0; i < 2; i++) { Thread2 t = new Thread2(caculate); t.start(); } } } //Thread1线程进行增加操作 class Thread1 extends Thread { private Caculate caculate; public Thread1() { } public Thread1(Caculate caculate) { this.caculate = caculate; } @Override public void run() { int i = 0; //死循环,手动停止 while(true) { try { caculate.add(); } catch (InterruptedException e) { e.printStackTrace(); } i++; System.out.println("加线程执行第 " + i + " 次"); } } } //Thread2进行减少操作。 class Thread2 extends Thread { private Caculate caculate; public Thread2() { } public Thread2(Caculate caculate) { this.caculate = caculate; } @Override public void run() { int i = 0; //死循环,手动停止 while(true) { try { caculate.sub(); } catch (InterruptedException e) { e.printStackTrace(); } i++; System.out.println("减线程执行第 " + i + " 次"); } } } // class Caculate { private int j = 0; //增加操作 public synchronized void add() throws InterruptedException { //当 j = 1 的时候说明不符合操作条件,要放弃对象锁。 while(j == 1) { wait(); System.out.println(); } j++; System.out.println(j); notify(); } //减少操作 public synchronized void sub() throws InterruptedException { //当j = 0 的时候说明不符合操作条件,放弃对象锁 while(j == 0) { wait(); System.out.println(); } j--; System.out.println(j); notify(); } }
以上代码并不能一直循环执行,按道理说应该是一直循环执行的。
为什么呢????????
这就涉及到了notify() 和 notifyAll()的其中一个区别了。
这个区别就是:调用 notify() 方法只能随机唤醒一个线程,调用notifyAll() 方法的唤醒所有的等待的线程。
比如这里,当一个线程在正常执行。。。假设这里正常执行完一个增加操作的线程,然后调用 notify() 方法 那么它会随机唤醒一个线程。
①、如果唤醒的是进行减少操作的线程,此时 j = 1,线程能够正常执行减少操作。
②、如果唤醒的是进行增加操作的线程,此时 j = 1,那么不符合增加操作的条件,他就会调用 wait() 方法。那么调用完wait()方法后程序就会发现已经没有被唤醒的线程了。唯一一个被唤醒的线程因不符合条件放弃了对象锁,其他线程又没有被唤醒。此时程序只能一直等到其他线程被唤醒,但是它等不到了。
解决:
把notify() 改成notifyAll() 这个问题就解决了。因为如果唤醒一个线程,但是这个线程因不符合执行条件而放弃对象,还有很多唤醒的线程。
所以,当多个(两个以上的)线程操作同一个对象的时候最好使用的notifyAll(),这样就不会出现上述的问题了。
发现一个问题,既然使用notify()会出问题那为什么不在每个地方的使用notifyAll()呢??这二者还有其他我没了解的区别吗???难道使用notifyAll() 会使性能大大下降???有待解决。