在实际运用中,我们设置的多个线程往往要相互合作、通信,共同完成业务需求。
当任务协作时,关键是这些任务之间的同步、执行顺序问题。我们可以使用互斥来解决!
java语言中,可以通过基类Object的wait()、notify()、notifyAll()方法,协调线程交叉执行。
wait() : 在其它线程调用此对象的 notify()
或 notifyAll()
方法前,导致当前线程等待。
notify() : 唤醒在此对象监视器上等待的单个线程。
因此所有任务必须等待相同的条件,以保证被唤醒的是恰当的那一个任务。
notifyAll() : 唤醒在此对象监视器上等待的所有线程,即只有等待这个锁的任务才会被唤醒。
因为这几个方法属于基类Object,它们操作的锁也是每个对象都有,故只能在同步控制方法或同步控制块中调用!
下面通过经典的生产者-消费者问题,来展示具体应用:
//生产者线程
public class Producer extends Thread{
private Store store;
public Producer(Store s){
this.store = s;
}
public void run(){
while(true){
store.add(); //添加货物
try{
//在指定的毫秒数内让当前正在执行的线程休眠
Thread.sleep(1000); //调用sleep()的时候,锁并没有释放
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
}
//消费者线程
public class Consumer extends Thread{
private Store store;
public Consumer(Store s){
this.store = s;
}
public void run(){
while(true){
store.remove(); //取走货物
try{
Thread.sleep(1500);
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
}
//在每个生产者线程添加货物前,检查是否满仓;同理,取货之前也检查是否空仓
public class Store{
private final int maxsize; //仓库的最大容量
private int count; //仓库的当前容量
public Store(int n){
maxsize = n;
count = 0;
}
//进货
public synchronized void add(){
while(count >= maxsize){
System.out.println("Store is full!");
try{
this.wait(); //如果满仓,就进入等待池
//wait()期间,对象锁是释放的! 所以在该对象中的其它synchronized方法可以在wait()期间被调用
}catch(InterruptedException e){
e.printStackTrace();
}
}
count++; //数量加1
System.out.println(Thread.currentThread().toString() + " put " + count);
this.notifyAll(); //通知所有的消费者线程来拿货
}
//取货
public synchronized void remove(){
while(count <= 0){
System.out.println("Store is empty!");
try{
this.wait(); //如果空仓,就进入等待池
}catch(InterruptedException e){
e.printStackTrace();
}
}
count--; //数量减1
System.out.println(Thread.currentThread().toString() + " get " + count);
this.notify(); //通知生产者添加货物
}
public static void main(String[] args){
Store store = new Store(5);
Thread pro = new Producer(store);
Thread con = new Consumer(store);
Thread pro2 = new Producer(store);
Thread con2 = new Consumer(store);
pro.setName("producer");
con.setName("consumer");
pro2.setName("producer2");
con2.setName("consumer2");
//启动线程
pro.start();
con.start();
pro2.start();
con2.start();
}
}
在等待条件改变时使用wait()方法让线程休眠,还节约了CPU资源。当其它线程调用该对象的notify()、notifyAll()方法时,线程重新唤醒。
另外,把wait()置于一个检查关键条件的while循环中,可以保证在条件不满足时,返回到wait()中。