程序要做的功能是:模似生产者与消费者。代码如下:
public class AddEggThread implements Runnable {
private Plate plate;
private Object egg = new Object();
public AddEggThread(Plate plate) {
this.plate = plate;
}
@Override
public void run() {
plate.addEgg(egg);
}
}
public class GetEggThread implements Runnable {
private Plate plate;
public GetEggThread(Plate plate) {
this.plate = plate;
}
@Override
public void run() {
plate.getEgg();
}
}
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Plate {
private List<Object> eggs = new ArrayList<Object>();
public synchronized Object getEgg() {
while(eggs.size() == 0) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Object egg = eggs.get(0);
eggs.clear(); //清空
notify(); // 唤醒阻塞队列的某线程进入就绪队列
System.out.println("拿到鸡蛋");
return egg;
}
public synchronized void addEgg(Object egg) {
while (eggs.size() > 0) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
eggs.add(egg);
notify();
System.out.println("放入鸡蛋");
}
public static void main(String[] args) {
Plate plate = new Plate();
ExecutorService exec = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
exec.execute(new GetEggThread(plate));
exec.execute(new AddEggThread(plate));
}
exec.shutdown();
}
}
执行程序,偶尔会产生死锁。回家后,在书上找到了解决问题的办法。先上代码:
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Plate {
private List<Object> eggs = new ArrayList<Object>();
public synchronized Object getEgg() {
while(eggs.size() == 0) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Object egg = eggs.get(0);
eggs.clear(); //清空
notifyAll(); // 唤醒阻塞队列的某线程进入就绪队列
System.out.println("拿到鸡蛋");
return egg;
}
public synchronized void addEgg(Object egg) {
while (eggs.size() > 0) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
eggs.add(egg);
notifyAll();
System.out.println("放入鸡蛋");
}
public static void main(String[] args) {
Plate plate = new Plate();
ExecutorService exec = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
exec.execute(new GetEggThread(plate));
exec.execute(new AddEggThread(plate));
}
exec.shutdown();
}
}
认真比较两段代码的差异,不难发现:notify() 改成 notifyAll()。为什么使用notify会导致死锁呢?可以解释为:
notify() 只会唤醒一个等待的线程,被唤醒的线程不一定能获当前释放的锁。
使用notify()而不是notifyAll()是一种优化。使用notify时,在众多等待的任务中只有一个会被唤醒。因此如果你希望使用notify(),就必须保证被唤醒的是恰当的任务。
在实际的程序中,很难做到使用notify()只唤醒恰当的任务(只有两个线程的情况除外)。因此,更多的时候我们使用notifyAll()。notifyAll() 将唤醒“所有正在等待的任务”这个说法并不正确。只唤醒等待当前锁的线程。