Thread线程从零认识到深层理解——生产者/消费者模型

线程系列目录

  1. Thread线程从零认识到深层理解——初识
  2. Thread线程从零认识到深层理解——六大状态
  3. Thread线程从零认识到深层理解——wait()与notify()
  4. Thread线程从零认识到深层理解——生产者/消费者模型
  5. Thread线程从零认识到深层理解——线程安全
  6. 线程池从零认识到深层理解——初识
  7. 线程池从零认识到深层理解——进阶

前言

Java中多线程的等待唤醒机制是一种经典的体现"生产者和消费者模型。例如:如工厂生产某种商品,当库存不足时就要补充生产,当仓库货满时,就要停止生产该类商品。
生产者/消费者模型需要满足如下几点:

  1. 生产者生产数据到缓冲区中,消费者从缓冲区中取数据。
  2. 如果缓冲区已经满了,则生产者线程阻塞,生产者通知消费者消费
  3. 如果缓冲区为空,那么消费者线程阻塞,消费者通知生产者生产

代码分析

我们用代码来模拟生产者和消费者之间的生产与消费行为,分析:

  1. 需要一个数据缓冲队列,缓存队列中的数据大小有上限,只能进行生成和消费两种操作
  2. 定义一个生产者线程,调用缓存队列的生产行为,需用synchronized限制
  3. 定义一个消费者线程,调用缓存队列的消费行为,需用synchronized限制
  4. 调用生产者生产时,如果缓存队列满,则生产线程阻塞进入等待。如果缓存队列未满,则进行具体的生产,方法完毕后释放锁,通知消费者消费
  5. 调用消费者消费时,如果缓存队列已空,则消费者线程阻塞进入等待。如果缓存队列未满,则进行消费操作,操作完毕释放锁,通知生产者生产。
public class ProducerConsumerTest {
    
    
    public static void main(String[] args) {
    
    
        BufferQueue<String> linkedHashQueue = new ReentrantLockQueue<>();
        ProducerThread producerThread = new ProducerThread(linkedHashQueue);
        ConsumerThread consumerThread = new ConsumerThread(linkedHashQueue);
        producerThread.start();//启动生产者线程
        consumerThread.start();//启动消费者线程

        try {
    
    
            Thread.sleep(4000);
            ProducerThread producerThread2 = new ProducerThread(linkedHashQueue,"追加");
            producerThread2.start();
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
    }
}

public class ProducerThread extends Thread {
    
    
    private BufferQueue<String> linkedHashQueue3;
    private String msg;

    public ProducerThread(BufferQueue<String> linkedHashQueue) {
    
    
        this.linkedHashQueue3 = linkedHashQueue;
    }

    public ProducerThread(BufferQueue<String> linkedHashQueue, String msg) {
    
    
        this.linkedHashQueue3 = linkedHashQueue;
        this.msg = msg;
    }

    @Override
    public void run() {
    
    
        for (int i = 0; i < 60; i++) {
    
    
            linkedHashQueue3.add(msg + "——西瓜_" + i);
        }
    }
}

public class ConsumerThread extends Thread {
    
    
    private BufferQueue<String> linkedHashQueue;
    public ConsumerThread(BufferQueue<String> linkedHashQueue) {
    
    
        this.linkedHashQueue = linkedHashQueue;
    }

    @Override
    public void run() {
    
    
        for (; ; ) {
    
    
            linkedHashQueue.remove();
        }
    }
}


public class LinkedHashQueue<T> extends BufferQueue<T> {
    
    

    /**
     * 缓冲区
     */
    private LinkedHashMap<Integer, T> linkedHashMap = new LinkedHashMap<>();

    /**
     * 生产者生产物质
     * 1.数据缓冲满,则停止生产
     * 2.数据缓冲未满,则唤醒等待队列中的消费线程进行消费,执行完synchronized方法才会释放锁
     *
     * @param msg 商品名
     */
    @Override
    public synchronized void add(T msg) {
    
    
        //如果缓存区达到最大数量,则阻塞生产者线程
        if (linkedHashMap.size() == maxCount) {
    
    
            try {
    
    
                wait();
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
        } else {
    
    
            notifyAll();//唤醒所有线程
        }

        linkedHashMap.put(putIndex, msg);
        System.out.println("生产一个产品,当前商品角标为:" + putIndex + "===文本为:" + msg + "===缓存长度为:" + linkedHashMap.size());
        putIndex = (putIndex + 1 >= maxCount) ? (putIndex + 1) % maxCount : putIndex + 1;
    }

    /**
     * 消费者进行消费
     * 1.数据缓冲区为0,进入等待状态
     * 2.消费消费物质后,
     */
    @Override
    public synchronized T remove() {
    
    
        if (linkedHashMap.size() == 0) {
    
    
            //如果缓存区没有数据,则阻塞消费线程
            try {
    
    
                wait();
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
        } else {
    
    
            notifyAll();//唤醒所有线程
        }

        Iterator<Map.Entry<Integer, T>> it = linkedHashMap.entrySet().iterator();
        T t = null;
        if (it.hasNext()) {
    
    
            Map.Entry<Integer, T> entry = it.next();
            t = entry.getValue();
            int index = entry.getKey();
            linkedHashMap.remove(index);
            System.out.println("消费一个产品,当前商品角标为:" + index + "===文本为:" + t + "===缓存长度为:" + linkedHashMap.size());
        }
        return t;
    }
}

public abstract class BufferQueue<T> {
    
    
    /**
     * 数据插入的角标
     */
    protected int putIndex = 0;
    /**
     * 缓存区最大长度
     */
    protected final int maxCount = 20;

    abstract void add(T msg);

    abstract T remove();

}

对于缓存队列也有另两种方式:

public class LinkedBlockQueue<T> extends BufferQueue<T> {
    
    
    /**
     * 缓冲区
     */
    private BlockingDeque<T> blockingDeque = new LinkedBlockingDeque<>(20);

    @Override
    public void add(T msg) {
    
    
        try {
    
    
            blockingDeque.put(msg);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
    }

    @Override
    public T remove() {
    
    
        T t = null;
        try {
    
    
            t = blockingDeque.take();
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        return t;
    }
}

public class ReentrantLockQueue<T> extends BufferQueue<T> {
    
    

    /**
     * 缓冲区
     */
    private LinkedHashMap<Integer, T> linkedHashMap = new LinkedHashMap<>();
    private Lock lock;
    private Condition addCondition;
    private Condition removeCondition;

    public ReentrantLockQueue() {
    
    
        lock = new ReentrantLock();
        addCondition = lock.newCondition();
        removeCondition = lock.newCondition();
    }

    @Override
    public void add(T msg) {
    
    
        lock.lock();
        try {
    
    
            if (linkedHashMap.size() == maxCount) {
    
    
                //如果缓存区达到最大数量,则阻塞生产者线程
                addCondition.await();//等待
            }
            linkedHashMap.put(putIndex, msg);
            System.out.println("生产一个产品,当前商品角标为:" + putIndex + "===文本为:" + msg + "===缓存长度为:" + linkedHashMap.size());
            putIndex = (putIndex + 1 >= maxCount) ? (putIndex + 1) % maxCount : putIndex + 1;

            //唤醒所有线程
            removeCondition.signalAll();
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            lock.unlock();
        }
    }

    @Override
    public T remove() {
    
    
        T t = null;
        lock.lock();
        try {
    
    
            if (linkedHashMap.size() == 0) {
    
    
                //如果缓存区没有数据,则阻塞消费线程
                removeCondition.await();//等待
            }
            Iterator<Map.Entry<Integer, T>> it = linkedHashMap.entrySet().iterator();
            if (it.hasNext()) {
    
    
                Map.Entry<Integer, T> entry = it.next();
                t = entry.getValue();
                int index = entry.getKey();
                linkedHashMap.remove(index);
                System.out.println("消费一个产品,当前商品角标为:" + index + "===文本为:" + t + "===缓存长度为:" + linkedHashMap.size());
            }
            addCondition.signalAll();//唤醒所有线程
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            lock.unlock();
        }
        return t;
    }
}

博客书写不易,您的点赞收藏是我前进的动力,千万别忘记点赞、 收藏 ^ _ ^ !

相关链接

  1. Thread线程从零认识到深层理解——初识
  2. Thread线程从零认识到深层理解——六大状态
  3. Thread线程从零认识到深层理解——wait()与notify()
  4. Thread线程从零认识到深层理解——生产者/消费者模型
  5. Thread线程从零认识到深层理解——线程安全
  6. 线程池从零认识到深层理解——初识
  7. 线程池从零认识到深层理解——进阶

猜你喜欢

转载自blog.csdn.net/luo_boke/article/details/107580475
今日推荐