java基础12:线程间通信---生产者消费者问题

经典的生产者消费者问题:

初级代码:通过改编读写线程程序得来。

class Goods{
    private int count=1;
    private String name;
    boolean flag=false;
    public synchronized  void set(String name)throws InterruptedException{
        if(flag){
            this.wait();
        }
        this.name=name+"---"+count++;
        System.out.println("生产者生产了"+this.name);
        flag=true;
        notify();
    }
    public synchronized void get()throws InterruptedException{
        if(!flag)
            this.wait();
        System.out.println("消费者消费了--------"+name+":"+count);
        flag=false;
        notify();
    }
}

class Producer implements Runnable{
    private Goods g;
    Producer(Goods g){
        this.g=g;
    }
    public void run(){
        while(true){
            try{
                g.set("+商品+");
            }catch(InterruptedException e){
                e.printStackTrace();
            }
        }
    }
}

class Consumer implements Runnable{
    private Goods g;
    Consumer(Goods g){
        this.g=g;
    }
    public void run(){
        while(true){
            try{
                g.get();
            }catch(InterruptedException e){
                e.printStackTrace();
            }
        }
    }
}

public class ProConsumDemo {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        
        Goods g=new Goods();
        Producer pro=new Producer(g);
        Consumer con=new Consumer(g);
        Thread t1=new Thread(pro);
       Thread t2=new Thread(pro);
        Thread t3=new Thread(con);
        Thread t4=new Thread(con);
        t1.start();
        t2.start();
        t3.start();
        t4.start();

    }

}



如果只有一个生产者一个消费者,运行结果是生产一个消费一个,但是如果有多个消费者多个生产者同时运行,运行结果如下:

这不符合生产一个消费一个的步骤,问题出在哪里呢?

该程序有四个线程,生产者1,生产者2,消费者3,消费者4,假设生产者1先抢到cpu,这时flag为false,继续执行生产代码,生产完后flag置为true,并且notify()一个线程,此时生产者1仍然有cpu的执行权,判断flag为true,这时wait了,这时活着的线程有生产者2,消费者3,和消费者4,假设生产者2获取到了cput的执行权,先判断flag,仍然为true,生产者2也wait(),这时假设消费者3抢到了cput执行权,判断flag,为false,执行消费代码,消费完后将flag置为false,并notify一个线程,因为生产者1先进入线程池,所以先唤醒生产者1,由于消费者3仍然有执行权,就回去判断flag,flag为false,所以消费者3wait(),这时活着的线程是生产者1和消费者4,假设消费者4先获取到cpu执行权,去判断flag,为false,所以也等待,这时生产者1得到cpu,由于只用if判断了一次flag,且生产者1停滞于wait()代码处,所以一开始执行就直接从wait之后执行生产代码,不再判断flag,生产完后,将flag置为true,notify一个线程,这时应该notify生产者2,此时生产者2也是直接从wait之后就开始执行生产代码,于是就产生了生产多个,消费一个的问题,同样道理也会出现生产一个消费多个的问题。

那么问题到底出在哪里呢?通过分析可以看出是因为唤醒了一个与上一个线程相同操作的线程,且该线程唤醒之后不再判断flag而是直接就开始执行之后代码,所以应该使用while循环来判断flag,但是如果只改while却可能会出现一直等待的情况,就是因为使用notify只能唤醒一个线程,如果唤醒一个与刚放弃cpu的线程相同操作的线程,那么这个线程又进入等待状态,那么所有的线程都进入等待状态。所以应该唤醒一个不同操作的线程,就需要用到notifyAll()。


改进代码:

class Goods{
    private int count=1;
    private String name;
    boolean flag=false;
    public synchronized  void set(String name)throws InterruptedException{
        while(flag){
            this.wait();
        }
        this.name=name+"---"+count++;
        System.out.println("生产者生产了"+this.name);
        flag=true;
        notifyAll();
    }
    public synchronized void get()throws InterruptedException{
        while(!flag)
            this.wait();
        System.out.println("消费者消费了--------"+name+":"+count);
        flag=false;
        notifyAll();
    }
}

class Producer implements Runnable{
    private Goods g;
    Producer(Goods g){
        this.g=g;
    }
    public void run(){
        while(true){
            try{
                g.set("+商品+");
            }catch(InterruptedException e){
                e.printStackTrace();
            }
        }
    }
}

class Consumer implements Runnable{
    private Goods g;
    Consumer(Goods g){
        this.g=g;
    }
    public void run(){
        while(true){
            try{
                g.get();
            }catch(InterruptedException e){
                e.printStackTrace();
            }
        }
    }
}

public class ProConsumDemo {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        
        Goods g=new Goods();
        Producer pro=new Producer(g);
        Consumer con=new Consumer(g);
        Thread t1=new Thread(pro);
        Thread t2=new Thread(pro);
        Thread t3=new Thread(con);
        Thread t4=new Thread(con);
        t1.start();
        t2.start();
        t3.start();
        t4.start();

    }

}

小小总结一下:

对于多个生产者和消费者,为什么要定义while判断标记?让被唤醒的线程再一次判断标记。
为什么定义notifyAll?因为需要唤醒对方线程,因为只用notify,容易出现只唤醒本方线程的情况,导致程序中的所有线程都等待。


JDK升级成JDK1.5后提供了一些新特性就可以有效的解决生产者消费者问题。

提供了Lock接口和Condition对象,Lock用来替代synchronized,Condition对象的方法用来替代wait、notify、notifyAll方法。

代码实现:

import java.util.concurrent.locks.*;

class Goods2{
    private int count=1;
    private String name;
    boolean flag=false;
    private Lock lock=new ReentrantLock();
    private Condition condition_pro=lock.newCondition();
    private Condition condition_con=lock.newCondition();
    public  void set(String name)throws InterruptedException{
        lock.lock();
        try{
            while(flag){
                condition_pro.await();
            }
            this.name=name+count++;
            System.out.println("生产者生产了"+this.name);
            flag=true;
            condition_con.signal();
        }
        finally{
            lock.unlock();
        }
    }
    public  void get()throws InterruptedException{
        lock.lock();
        try{
            while(!flag)
                condition_con.await();
            System.out.println("消费者消费了"+this.name);
            flag=false;
            condition_pro.signal();
        }
        finally{
            lock.unlock();
        }
    }
}

class Producer2 implements Runnable{
    private Goods2 g;
    Producer2(Goods2 g){
        this.g=g;
    }
    public void run(){
        while(true){
            try{
                g.set("+商品+");
            }catch(InterruptedException e){
                e.printStackTrace();
            }
        }
    }
}

class Consumer2 implements Runnable{
    private Goods2 g;
    Consumer2(Goods2 g){
        this.g=g;
    }
    public void run(){
        while(true){
            try{
                g.get();
            }catch(InterruptedException e){
                e.printStackTrace();
            }
        }
    }
}
public class LockConditionDemo {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub

        Goods2 g=new Goods2();
        Producer2 pro=new Producer2(g);
        Consumer2 con=new Consumer2(g);
        Thread t1=new Thread(pro);
        Thread t2=new Thread(pro);
        Thread t3=new Thread(con);
        Thread t4=new Thread(con);
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }

}
运行结果:



JDK1.5中提供了多线程升级解决方案:
将同步synchronized替换成现实Lock操作。将Object中的wait、notify、notifyAll替换成了Condition对象。该对象可以通过Lock锁进行获取。



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值