使用多线程实现一个阻塞队列

原理:使用jvm锁机制和一个普通队列共同实现一个阻塞队列

package com.es.demo;
import lombok.extern.slf4j.Slf4j;
import java.util.LinkedList;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 使用多线程实现一个阻塞队列
 *
 * @author renpl
 */
@Slf4j
public class MyBlockQueue {

    // 初始化一个普通队列
    private final LinkedList<Object> list = new LinkedList<>();

    // 初始化一个计数器用于记录集合中添加的元素
    private final AtomicInteger counter = new AtomicInteger();

    // 初始化队列元素的最小数
    private int minSize = 0;

    // 初始化队列元素的最大数
    private int maxSize;

    // 初始化一个对象,用于加锁
    private Object lock = new Object();

    // 构造函数初始化一个制定大小的队列
    public MyBlockQueue(int maxSize) {
        this.maxSize = maxSize;
    }

    /**
     * 阻塞队列添加方法
     *
     * @param obj
     * @throws InterruptedException
     */
    private void put(Object obj) {
        try {
            synchronized (lock) {
                while (maxSize == counter.get()) {
                    log.debug("队列已满");
                    lock.wait();
                }
                // 队列中添加元素
                list.add(obj);
                // 计数器加1
                counter.incrementAndGet();
                // 唤醒其他正在排队向队列中添加元素的线程
                lock.notify();
            }
        } catch (InterruptedException e) {
            log.error("队列中添加元素发生异常", e);
        }
    }

    /**
     * 阻塞队列获取并移除元素
     *
     * @return
     * @throws InterruptedException
     */
    private Object take() {
        try {
            synchronized (lock) {
                while (minSize == counter.get()) {
                    log.debug("此时队列中没有元素");
                    lock.wait();
                }
                // 获取队列中的元素并且从队列中移除
                Object obj = list.removeFirst();
                // 计数器减1
                counter.decrementAndGet();
                // 唤醒其他正在排队从队列中获取元素的线程
                lock.notify();
                return obj;
            }
        } catch (InterruptedException e) {
            log.error("获取并移除队列中元素发生异常", e);
        }
        return null;
    }

    /**
     * 测试
     *
     * @param args
     */
    public static void main(String[] args) {
        // 主线程先把队列填满
        MyBlockQueue blockQueue = new MyBlockQueue(2);
        blockQueue.put("one");
        blockQueue.put("two");

        // 启动一个线程向填满的队列中新添加元素
        new Thread(new Runnable() {
            @Override
            public void run() {
                log.info("新添加的元素:three");
                blockQueue.put("three");
            }
        }).start();

        // 获取并移除队列的元素,从而让上一个线程向队列中添加元素
        new Thread(new Runnable() {
            @Override
            public void run() {
                String item = (String) blockQueue.take();
                log.info("获取到队列的元素:{}", item);
            }
        }).start();
    }
}
运行结果:

  09:56:56.848 [Thread-0] DEBUG com.es.demo.MyBlockQueue - 队列已满
  09:56:56.851 [Thread-0] INFO com.es.demo.MyBlockQueue - 新添加的元素:three
  09:56:56.851 [Thread-1] INFO com.es.demo.MyBlockQueue - 获取到队列的元素:one

  执行流程分析:主线程向blockQueue队列中添加了one,two两个元素,此时blockQueue队列已满;

线程Thread-0再次在09:56:56.848时刻向blockQueue中添加元素three,此时队列已阻塞了,控制台打印:

队列已满;这时候线程Thread-1在09:56:56.851时刻从队列中获取并移除one元素,同一时刻线程Thread-0

被唤醒向blockQueue中添加元素three,由于线程Thread-0执行的比线程Thread-1快,所以one元素被移除后

立马就添加元素three,打印:新添加的元素:three,最后又切换至线程Thread-1,执行打印:获取到队列的元素:one

猜你喜欢

转载自blog.csdn.net/weixin_39352976/article/details/108659075