java的生产者消费者代码实现(使用wait()、notify()、notifyAll())、阻塞队列实现

生产者消费者问题也是等待唤醒机制,是一个十分经典的多线程协作的模式。
一、常见方法
java的生产者消费者代码实现(使用wait()、notify()、notifyAll())、阻塞队列实现_第1张图片
例如,设生产者消费者问题中的缓冲池大小为1。首先利用操作系统中的信号量机制对问题进行分析:
java的生产者消费者代码实现(使用wait()、notify()、notifyAll())、阻塞队列实现_第2张图片
java的生产者消费者代码实现(使用wait()、notify()、notifyAll())、阻塞队列实现_第3张图片
java代码实现:
缓冲池:

public class Buffer {
    //缓冲池大小为1

 //标志缓冲池中是否有产品的变量,0表示无产品,1表示有产品
   public static int goodFlag = 0;

   //总次数,表示消费者生产产品的上限
    public static int count = 10;

    //锁对象,互斥地访问goodFlag变量
    public static Object lock = new Object();

//同步方法,用于互斥地使用缓冲池,使用同步方法时阻塞和唤醒操作底层已经实现了
    public synchronized static void useBuffer(String name,Thread t){
        if("生产者".equals(name)){
            System.out.println(t.getName()+"生产了一个产品");
        }
        else if("消费者".equals(name))
        {
            System.out.println(t.getName()+"在消费产品,还能再消费"+Buffer.count+"个产品");
        }
    }

}

生产者:
```java
public class Producer extends Thread{
    @Override
    public void run() {
         while(true){
                synchronized (Buffer.lock){
                    if(Buffer.count==0){
                        break;
                    }
                    else{
                        if(Buffer.goodFlag==1){
                            try {
                                //缓冲池内已有产品,生产者进程阻塞
                                Buffer.lock.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        else{
                            Buffer.useBuffer("生产者",Thread.currentThread());
                            Buffer.goodFlag=1;
                            Buffer.lock.notifyAll();//唤醒锁的阻塞队列上的线程
                        }
                    }
                }
         }
    }
}


消费者:

public class Consumer extends Thread{
    @Override
    public void run() {
        while(true) {
            synchronized (Buffer.lock){//Buffer.lock为锁
                if(Buffer.count == 0)
                {
                    //生产者已经生产了10个产品
                    break;
                }
                else{
                    if(Buffer.goodFlag == 0)
                    {
                        try {
                            Buffer.lock.wait();//使用锁对象使对象进入阻塞队列
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }else{
                        Buffer.count--;
                        Buffer.useBuffer("消费者",Thread.currentThread());
                        Buffer.lock.notifyAll();//唤醒锁对象的阻塞队列中的所有线程
                        Buffer.goodFlag=0;
                    }
                }
            }
        }
    }
}


测试运行:

```java
public class TestThreads {
    public static void main(String[] args) {
        Producer p1 = new Producer();
        Consumer c1 = new Consumer();
        Producer p2 = new Producer();
        Consumer c2 = new Consumer();
        c1.setName("消费者1");
        p1.setName("生产者1");
        c2.setName("消费者2");
        p2.setName("生产者2");
        c1.start();
        p1.start();
        c2.start();
        p2.start();
    }
}

效果如下:
java的生产者消费者代码实现(使用wait()、notify()、notifyAll())、阻塞队列实现_第4张图片
二、阻塞队列实现等待唤醒机制
阻塞队列实现了下图中的红色框的四个接口:
java的生产者消费者代码实现(使用wait()、notify()、notifyAll())、阻塞队列实现_第5张图片
在这里插入图片描述

接口不能直接定义对象,需要用上图中两个蓝色框中的两个实现类定义阻塞队列的对象。
阻塞队列相当于连接生产者和消费者之间的通道,相当于缓冲池,可以理解为缓冲区大小大于1,且是一个队列。
使用阻塞队列简化上述代码。
生产者:

public class Producer extends Thread{
    ArrayBlockingQueue<String> queue;
    public Producer(ArrayBlockingQueue<String> queue){
        this.queue=queue;
    }
    @Override
    public void run() {
         while(true){
             try {
                 queue.put("产品");//将产品放到阻塞队列中
                 System.out.println(getName()+"生产了一个产品放到缓冲池中");
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
         }
    }
}

消费者:

public class Consumer extends Thread{
    ArrayBlockingQueue<String> queue;
    public Consumer(ArrayBlockingQueue<String> queue){
        this.queue=queue;
    }
    @Override
    public void run() {
        while(true) {
            try {
                String good = queue.take();//从阻塞队列中取产品
                System.out.println(getName()+"消费了一个"+good);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

测试运行:

public class TestThreads {
    public static void main(String[] args) {
        ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(1);
                Producer producer1 = new Producer(queue);
        Producer producer2 = new Producer(queue);
        Consumer consumer1 = new Consumer(queue);
        Consumer consumer2 = new Consumer(queue);
        producer1.setName("生产者1");
        consumer1.setName("消费者1");
        producer2.setName("生产者2");
        consumer2.setName("消费者2");
        producer1.start();
        consumer1.start();
        producer2.start();
        consumer2.start();
    }
}

结果:
java的生产者消费者代码实现(使用wait()、notify()、notifyAll())、阻塞队列实现_第6张图片
可以看到,运行结果显示阻塞队列大小为1,生产者却可以连续生产,其实并不是这样。出现这种状况的原因如下图,生产者放产品和打印输出语句并不一定一次性执行完,由于多个线程并发执行导致这种错觉。
java的生产者消费者代码实现(使用wait()、notify()、notifyAll())、阻塞队列实现_第7张图片

你可能感兴趣的:(java,jvm)