在实际运用中,我们设置的多个线程往往要相互合作、通信,共同完成业务需求。
当任务协作时,关键是这些任务之间的同步、执行顺序问题。我们可以使用互斥来解决!
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()中。