最佳实践:总是使用notifyAll而不使用notify
示例代码:交替打印0和1的程序
package com.example.app;
import java.util.Date;
public class MyCounter2 {
private int counter;
public int getCounter() {
return counter;
}
public void setCounter(int counter) {
this.counter = counter;
}
public synchronized void increase(){
String name=Thread.currentThread().getName();
System.out.println(System.currentTimeMillis()+" in thread: "+name+ " "+this.getCounter());
while(this.getCounter()>0){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.counter++;
System.out.println(System.currentTimeMillis()+" "+name+" "+this.counter);
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.notify();
}
public synchronized void decrease(){
String name=Thread.currentThread().getName();
System.out.println(System.currentTimeMillis()+" in thread: "+name+ " "+this.getCounter());
while(this.getCounter()<1){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.counter--;
System.out.println(System.currentTimeMillis()+" "+name+" "+this.counter);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.notify();
}
}
package com.example.app;
public class NotifyTest2 {
private static MyCounter2 myCounter = new MyCounter2();
public static void main(String[] args) throws InterruptedException {
Thread t1 = new ThreadA(myCounter);
Thread t2= new ThreadB(myCounter);
Thread t3=new ThreadC(myCounter);
Thread t4 = new ThreadD(myCounter);
t1.start();
t2.start();
t3.start();
t4.start();
t1.join();
t2.join();
t3.join();
t4.join();
}
}
class ThreadA extends Thread{
private MyCounter2 myCounter;
public ThreadA(MyCounter2 myCounter){
this.myCounter=myCounter;
setName("thread-A");
}
@Override
public void run(){
while(true){
myCounter.increase();
}
}
}
class ThreadB extends Thread{
private MyCounter2 myCounter;
public ThreadB(MyCounter2 myCounter){
this.myCounter=myCounter;
setName("thread-B");
}
@Override
public void run(){
while(true){
myCounter.decrease();
}
}
}
class ThreadC extends Thread{
private MyCounter2 myCounter;
public ThreadC(MyCounter2 myCounter){
this.myCounter=myCounter;
setName("thread-C");
}
@Override
public void run(){
while(true){
myCounter.increase();
}
}
}
class ThreadD extends Thread{
private MyCounter2 myCounter;
public ThreadD(MyCounter2 myCounter){
this.myCounter=myCounter;
setName("thread-D");
}
@Override
public void run(){
while(true){
myCounter.decrease();
}
}
}
一次执行结果及执行时序图分析
可见,使用notify时,可能最后4个线程都处于锁对象的等待队列(WaiSet)中,程序阻塞。原因是notify只会唤醒wait set中的1个线程。
将程序中的notify改成notifyAll,就不会出现上述的4个线程都处于wait set中的情况。