上篇讲述线程的同步,但在多数情况下,仅仅同步是不够的,还需要线程间的协作.
在java多线程中,锁(monitor)涉及到就绪队列和阻塞队列,就绪队列保存将要获取同步锁的线程,阻塞队列存储处于阻塞状态的线程. 当线程被唤醒(notify)后,进入就绪队列,等待cpu调度;当线程wait后,就会进入阻塞队列,等待下次被唤醒.
我们可以利用wait和notify的特性,来解决经典的生产者/消费者问题.
package com.producer;
import java.util.ArrayList;
import java.util.List;
public class ProducerConsumer {
public static void main(String[] args){
Plate plate = new Plate(5);
new Thread(new Consumer(plate)).start();
new Thread(new Producer(plate)).start();
}
}
class Plate {
int[] ary;
int size;
Plate(int num){
ary = new int[num];
size = 0;
}
public synchronized Object get(){
while(size == 0){
System.out.println("empty!");
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
size--;
int res = ary[size];
notify();
System.out.println("get " + res);
return res;
}
public synchronized void put(int num){
while(size == ary.length){
System.out.println("full!");
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
ary[size] = num;
size++;
notify();
System.out.println("put " + num);
}
}
class Consumer implements Runnable{
Plate plate;
Consumer(Plate plate){
this.plate = plate;
}
public void run(){
for(int i=0; i<10; i++){
plate.get();
}
}
}
class Producer implements Runnable{
Plate plate;
Producer(Plate plate){
this.plate = plate;
}
public void run(){
for(int i=0; i<10; i++){
plate.put(i);
}
}
}
大家注意到没,在调用wait方法时,都是用while来检测条件,而不是if,因为某些特定情况下,线程也会被唤醒,使用while来循环检测更稳妥;wait和notify必须在synchronized内部使用,换句话说,需要先获取同步锁.
wait()
使当前线程处于等待状态,直到其他线程调用该对象notify或notifyAll方法;
当前线程必须拥有该对象的monitor,调用该方法后线程释放monitor的所有权并等待,直到其他线程通过notify或notifyAll方法唤醒处于等待状态的线程;然后该线程重新获得monitor并继续执行.
对于单个参数的版本,可能会发生中断或虚假唤醒,所以该方法应该在循环中使用.
synchronized (obj) {
while (<condition does not hold>)
obj.wait();
... // Perform action appropriate to condition
}
notify()
唤醒在该对象monitor上等待的单个线程,若所有线程都处于等待状态,随机选择某个线程;线程调用wait方法后,在该对象的monitor上等待.
唤醒的线程直到当前线程释放该对象的锁定后才能执行,唤醒的线程将以常规的方式与该对象上主动同步的其他线程进行竞争.
该方法必须在获取该对象monitor的线程中执行,获取的方法如下
- 执行synchronized修饰的方法
- 执行该对象作为synchronized的锁包含的代码块
notifyAll()
唤醒所有在该对象monitor上等待的线程