本文讲述Java中的线程同步和生产者消费者问题,其中主要涉及线程同步和wait()、notify()方法的用法。
wait和notify方法只能用在线程同步中,wait和notify是object的方法,因此任何对象都能调用,当一个线程中调用wait方法时会将该线程阻塞,将cpu资源让给其他线程,关键的一点是在线程阻塞前会将持有的锁释放,这一点是和sleep方法的很大区别,sleep方法使线程睡眠的话是不会主动释放锁的,直到sleep时间到或被中断线程唤醒。wait方法将持有的锁释放然后阻塞调用该wait方法的线程,直到设置的wait时间耗尽或者被notify唤醒重新持有锁,调用wait的线程被阻塞后,CPU执行其他线程,在其他线程中调用了notify或notifyAll后会唤醒被阻塞的线程。
生产者消费者问题中,有一个先后问题,必须先生产再消费,要吃饭必须先种出稻谷,生产者和消费者问题和Linux的应用编程中的竞争互斥问题是一样的。生产为一个线程,消费为一个线程,有一个仓库存放产品,生产出的产品放在仓库中,要消费也要去仓库中取产品,所以生产线程和消费线程是共同持有仓库这个锁的。当生产者持有锁的时候不能消费,在消费线程持有锁的时候不能生产。当仓库满了以后还意图继续生产则调用wait将生产线程阻塞,释放锁,将CPU让给消费线程,消费线程消费了产品以后仓库不再是满的状态,然后消费线程调用notify唤醒生产线程可以继续生产,当消费线程执行完以后释放锁,生产线程唤醒了重新获得了刚被消费线程释放的锁,然后生产线程就可以继续执行了;(^_^)当消费线程将仓库中的产品消费完后,消费线程意图再次消费却发现仓库空了不能消费,所以调用wait释放锁将自己阻塞,生产线程获得cpu执行权和锁去生产产品,生产了产品以后放到仓库调用notify唤醒消费线程,告诉消费线程仓库有东东了,你可以去消费了,当然是要等生产线程执行完释放锁以后消费线程才有机会重新获得锁并去消费。
import java.util.*; public class lesson911 { public static void main(String [] args) { StoreHouse sh = new StoreHouse(); Producer p =new Producer(sh); //创建生产者线程 Consumer c = new Consumer(sh); //创建消费线程 new Thread(p).start(); //启动线程 new Thread(c).start(); //无论哪个线程先启动都必须是先生产再消费 } } class Product { //产品 int id; Product(int id) { this.id = id; } public String toString() { //重新定义toString方法,其实这个程序中不需要,没用到。 return "product:"+id; } } class StoreHouse { //定义仓库 Product [] ArrProduct = new Product[6]; //仓库只能容纳6个产品 int index=0; int num_push=0,num_pop=0; public synchronized void push(Product product) { //生产:将产品放到仓库 while(index==ArrProduct.length) { try { System.out.println("$$仓库满了$$(^_^)"); this.wait(); //当仓库满了以后调用wait,阻塞意图再次生产的线程。 }catch(InterruptedException e) { e.printStackTrace(); } } this.notify(); //正常生产,调用notify通知消费线程仓库已有产品可去仓库消费 //System.out.println("notify 消费者"); ArrProduct[index] = product; index++; num_push++; System.out.println("####生产了产品###:"+(num_push-1)); } public synchronized Product pop() { //消费:将产品从仓库取出 while(index==0) { try { System.out.println("&&仓库空了&&(@_@)"); this.wait(); //当仓库空了以后,调用wait阻塞意图再次消费的消费线程 }catch(InterruptedException e) { e.printStackTrace(); } } this.notify(); //正常消费,调用notify告诉生产线程现在仓库已经有空位可以继续生产 // System.out.println("notify 生产者"); index--; num_pop++; System.out.println("*******消费了产品*******:"+(num_pop-1)); return ArrProduct[index]; } } class Producer implements Runnable { //生产者线程 StoreHouse sh = null; Producer(StoreHouse sh) { //生产线程和消费线程都是操作的同一个仓库。 this.sh = sh; } public void run() { for(int i=0;i<15;i++) { Product product = new Product(i); sh.push(product); //往仓库中添加产品 try { Thread.sleep((int)(Math.random()*1000)); }catch(InterruptedException e) { e.printStackTrace(); } } } } class Consumer implements Runnable { //消费者线程 StoreHouse sh = null; Consumer(StoreHouse sh) { this.sh = sh; } public void run() { for(int i=0;i<15;i++) { Product product = sh.pop(); try { Thread.sleep((int)(Math.random()*1000)); }catch(InterruptedException e) { e.printStackTrace(); } } } }
运行结果如下图所示