多线程通信(二)生产者/消费者模式

本文介绍了基于wait/notify实现的Java生产者/消费者模式。通过代码示例展示了生产者和消费者一对一及多对多的通信情况,讨论了可能出现的假死现象,并提出使用notifyAll()避免假死的问题。下篇将继续改进通信的交叉进行。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        生产者/消费者模式其原理是基于wait/notify实现的。

        通过代码更能便于理解,废话不多说,上代码。

        生成者和消费者一对一模式

        生产者

public class Producer {

    private String obj;

    public Producer(String obj){

        super();
        this.obj = obj;
    }

    public void buildProduct(){

        try {
            synchronized (obj){
                // 如果有产品,生产者等待
                if(StringUtils.isNotBlank(ProductObj.product)){
                    Thread.sleep(1000);
                    System.out.println("生产者进入等待状态。。。。。。");
                    obj.wait();
                }

                //生产产品并通知消费者
                Thread.sleep(1000);
                System.out.println("生产者生产产品");
                ProductObj.product = "我有产品";
                obj.notify();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

生产者线程

public class ProducerThread extends Thread{

    private Producer producer;

    public ProducerThread(Producer producer){

        super();
        this.producer = producer;
    }

    @Override
    public void run(){

        while(true){
            producer.buildProduct();
        }
    }
}

消费者

public class Consumer {

    private String obj;

    public Consumer(String obj){

        super();
        this.obj = obj;
    }

    public void consumeProduct(){

        try{
            synchronized (obj){
                // 如果没有产品,消费者等待
                if(StringUtils.isBlank(ProductObj.product)){
                    Thread.sleep(1000);
                    System.out.println("消费者进入等待状态------");
                    obj.wait();
                }

                // 消费产品并通知生产者
                Thread.sleep(1000);
                System.out.println("消费者消费产品");
                ProductObj.product = "";
                obj.notify();
            }
        } catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

消费者线程

public class ConsumerThread extends Thread{

    private Consumer consumer;

    public ConsumerThread(Consumer consumer){

        super();
        this.consumer = consumer;
    }

    @Override
    public void run(){

        while(true){
            consumer.consumeProduct();
        }
    }
}

产品容器


public class ProductObj {
    public static String product = new String();
}

测试类

public class Test2 {

    public static void main(String[] args){

        String obj = new String();

        Producer producer = new Producer(obj);
        Consumer consumer = new Consumer(obj);

        ProducerThread producerThread = new ProducerThread(producer);
        producerThread.start();
        ConsumerThread consumerThread = new ConsumerThread(consumer);
        consumerThread.start();
    }
}

结果: 不停的在生产和消费产品。


        生产者和消费者多对多模式

我们修改测试类,再修改下生产者和消费者类的打印方式,看下多对多的情况

生产者

   

public class Producer {

    private String obj;

    public Producer(String obj){

        super();
        this.obj = obj;
    }

    public void buildProduct(){

        try {
            synchronized (obj){
                // 如果有产品,生产者等待
                if(StringUtils.isNotBlank(ProductObj.product)){
                    System.out.println(Thread.currentThread().getName() + "======Wait");
                    obj.wait();
                }

                //生产产品并通知消费者
//                Thread.sleep(1000);
                System.out.println(Thread.currentThread().getName() + "======Runnable");
                ProductObj.product = "我有产品";
                obj.notify();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

消费者

public class Consumer {

    private String obj;

    public Consumer(String obj){

        super();
        this.obj = obj;
    }

    public void consumeProduct(){

        try{
            synchronized (obj){
                // 如果没有产品,消费者等待
                if(StringUtils.isBlank(ProductObj.product)){
                    System.out.println(Thread.currentThread().getName() + "------Wait");
                    obj.wait();
                }

                // 消费产品并通知生产者
//                Thread.sleep(1000);
                System.out.println(Thread.currentThread().getName() + "-----Runnable");
                ProductObj.product = "";
                obj.notify();
            }
        } catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

测试类

public class Test2 {

    public static void main(String[] args){

        String obj = new String();

        Producer producer = new Producer(obj);
        Consumer consumer = new Consumer(obj);

        ProducerThread[] producerThreads = new ProducerThread[2];
        ConsumerThread[] consumerThreads = new ConsumerThread[2];

        for(int i = 0; i < 5; i++){

            producerThreads[i] = new ProducerThread(producer);
            producerThreads[i].setName("生产者" + i);
            consumerThreads[i] = new ConsumerThread(consumer);
            consumerThreads[i].setName("消费者" + i);

            producerThreads[i].start();
            consumerThreads[i].start();
        }


//
//        ProducerThread producerThread = new ProducerThread(producer);
//        producerThread.start();
//        ConsumerThread consumerThread = new ConsumerThread(consumer);
//        consumerThread.start();
    }
}


这是结果


上面的结果中标红的地方可以看出消费者0被唤醒后运行,消费产品,然后又竞争到锁,进入wait状态,然后消费者1竞争获得锁,但是这个时候是没有产品的,但是也运行runnable了,可以看出,被唤醒的线程是从wait()方法后继续执行的,也看出了代码的不合理性,对消费者和生产者类稍作改动。

                // 如果有产品,生产者等待
                //if(StringUtils.isNotBlank(ProductObj.product)){
                while(StringUtils.isNotBlank(ProductObj.product)){    
                    System.out.println(Thread.currentThread().getName() + "======Wait");
                    obj.wait();
                }
                // 如果没有产品,消费者等待
                //if(StringUtils.isBlank(ProductObj.product)){
                while(StringUtils.isBlank(ProductObj.product)){
                    System.out.println(Thread.currentThread().getName() + "------Wait");
                    obj.wait();
                }

运行结果


这样就不会出现上面描述的情况了,但是注意新代码红色框的地方,所有的线程都进入了等待状态,这种情况就是有几率发生的现象--假死委屈,有兴趣的可以自己试试。造成假死的原因是因为消费者的notify()方法唤醒的并不一定是生产者,也有可能是消费者,而生产者的notify()唤醒也不一定是消费者,也有可能是生产者惊恐,所以可能经过N次等待唤醒后可能所有线程全部是wait状态,解决假死其实很简单,就是在生产者和消费者中使用notifyAll()方法,notifyAll()方法是用来唤醒所有等待线程的,这样就不会出现假死现象了。

                //obj.notify();
                obj.notifyAll();

部分结果截屏,结果没有问题,如果消费者和生产者能交叉进行就更好了。下篇继续改进。


下篇内容:多线程之通知/等待交叉进行

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值