阻塞队列笔记

阻塞队列

概念

队列

队列就可以想成是一个数组,从一头进入,一头出去,排队买饭

阻塞队列

BlockingQueue 阻塞队列,排队拥堵,首先它是一个队列,而一个阻塞队列在数据结构中所起的作用大致如下图所示:

线程1往阻塞队列中添加元素,而线程2从阻塞队列中移除元素

  • 当阻塞队列是空时,从队列中获取元素的操作将会被阻塞

    • 当蛋糕店的柜子空的时候,无法从柜子里面获取蛋糕
  • 当阻塞队列是满时,从队列中添加元素的操作将会被阻塞

    • 当蛋糕店的柜子满的时候,无法继续向柜子里面添加蛋糕了

也就是说 试图从空的阻塞队列中获取元素的线程将会被阻塞,直到其它线程往空的队列插入新的元素

同理,试图往已经满的阻塞队列中添加新元素的线程,直到其它线程往满的队列中移除一个或多个元素,或者完全清空队列后,使队列重新变得空闲起来,并后续新增

为什么要用?

去海底捞吃饭,大厅满了,需要进候厅等待,但是这些等待的客户能够对商家带来利润,因此我们非常欢迎他们阻塞

在多线程领域:所谓的阻塞,在某些清空下会挂起线程(即阻塞),一旦条件满足,被挂起的线程又会自动唤醒

为什么需要BlockingQueue

好处是我们不需要关心什么时候需要阻塞线程,什么时候需要唤醒线程,因为这一切BlockingQueue都帮你一手包办了

在concurrent包发布以前,在多线程环境下,我们每个程序员都必须自己取控制这些细节,尤其还要兼顾效率和线程安全,而这会给我们的程序带来不小的复杂度。

架构

// 你用过List集合类
// ArrayList集合类熟悉么?
// 还用过 CopyOnWriteList 和 BlockingQueue

BlockingQueue阻塞队列是属于一个接口,底下有七个实现类

  • ArrayBlockQueue:由数组结构组成的有界阻塞队列
  • LinkedBlockingQueue:由链表结构组成的有界(但是默认大小 Integer.MAX_VALUE)的阻塞队列
    • 有界,但是界限非常大,相当于无界,可以当成无界
  • PriorityBlockQueue:支持优先级排序的无界阻塞队列
  • DelayQueue:使用优先级队列实现的延迟无界阻塞队列
  • SynchronousQueue:不存储元素的阻塞队列,也即单个元素的队列
    • 生产一个,消费一个,不存储元素,不消费不生产
  • LinkedTransferQueue:由链表结构组成的无界阻塞队列
  • LinkedBlockingDeque:由链表结构组成的双向阻塞队列

这里需要掌握的是:ArrayBlockQueue、LinkedBlockingQueue、SynchronousQueue

BlockingQueue核心方法

抛出异常当阻塞队列满时:在往队列中add插入元素会抛出 IIIegalStateException:Queue full 当阻塞队列空时:再往队列中remove移除元素,会抛出NoSuchException
特殊性插入方法,成功true,失败false 移除方法:成功返回出队列元素,队列没有就返回空
一直阻塞当阻塞队列满时,生产者继续往队列里put元素,队列会一直阻塞生产线程直到put数据or响应中断退出, 当阻塞队列空时,消费者线程试图从队列里take元素,队列会一直阻塞消费者线程直到队列可用。
超时退出当阻塞队列满时,队里会阻塞生产者线程一定时间,超过限时后生产者线程会退出

SynchronousQueue

SynchronousQueue没有容量,与其他BlockingQueue不同,SynchronousQueue是一个不存储的BlockingQueue,每一个put操作必须等待一个take操作,否者不能继续添加元素

阻塞队列的用处

生产者消费者模式

一个初始值为0的变量,两个线程对其交替操作,一个加1,一个减1,来5轮

关于多线程的操作,我们需要记住下面几句

  • 线程 操作 资源类
  • 判断 干活 通知
  • 防止虚假唤醒机制

我们下面实现一个简单的生产者消费者模式

/**
 * @author HX
 * @title: ProdConsumerTraditionDemo
 * @projectName spring_cloud1x
 * @date 2022/5/11 14:30
 * <p>
 * 一个初始值为0的变量,两个线程对其交替操作,一个加1,一个减1,来5轮
 */
class ShareData {
    private Integer num = 0;
    private Lock lock = new ReentrantLock();

    private Condition condition = lock.newCondition();

    //加一
    public void add() {
        lock.lock();
        try {
            // 判断 while 防止虚假唤醒机制
            while (num != 0) {
                condition.await();
            }
            // 干活
            num++;
            System.out.println(Thread.currentThread().getName() + "\t " + num);

            //通知
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    //减一
    public void reduce() {
        lock.lock();
        try {
            // 判断 while 防止虚假唤醒机制
            while (num == 0) {
                condition.await();
            }
            // 干活
            num--;
            System.out.println(Thread.currentThread().getName() + "\t " + num);

            //通知
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

public class ProdConsumerTraditionDemo {
    /**
     * @param args 线程 操作 资源类
     *             判断 干活 通知
     *             防止虚假唤醒机制
     */
    public static void main(String[] args) {
        ShareData shareData = new ShareData();
        new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                shareData.add();
            }
        }, "AAAA").start();

        new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                shareData.reduce();
            }
        }, "BBBB").start();
    }
}

生成者和消费者3.0

在concurrent包发布以前,在多线程环境下,我们每个程序员都必须自己去控制这些细节,尤其还要兼顾效率和线程安全,则这会给我们的程序带来不小的时间复杂度

现在我们使用新版的阻塞队列版生产者和消费者,使用:volatile、CAS、atomicInteger、BlockQueue、线程交互、原子引用

/**
 * @author HX
 * @title: ProdConsumerBlockingQueueDemo
 * @projectName spring_cloud1x
 * @date 2022/5/13 9:45
 * 生产者消费者  阻塞队列版
 * 使用:volatile、CAS、atomicInteger、BlockQueue、线程交互、原子引用
 */
class MySource {
    private volatile boolean FLAGE = true;
    private AtomicInteger atomicInteger = new AtomicInteger(0);
    BlockingQueue<String> queue = null;

    // 而应该采用依赖注入里面的,构造注入方法传入
    public MySource(BlockingQueue<String> queue) {
        this.queue = queue;
        System.out.println(Thread.currentThread().getName());
    }

    //生产
    public void myProd() throws Exception {
        String data = "";
        boolean offer = false;
        //判断
        while (FLAGE) {
            data = atomicInteger.incrementAndGet() + "";
            offer = queue.offer(data, 2L, TimeUnit.SECONDS);
            if (offer) {
                System.out.println(Thread.currentThread().getName() + "\t 插入成功 值--》" + data);
            } else {
                System.out.println(Thread.currentThread().getName() + "\t 插入失败");
            }
            //2秒执行一次
            Thread.sleep(1000);
        }
        System.out.println(Thread.currentThread().getName() + "\t 停止生产,表示FLAG=false,生产介绍");

    }

    /**
     * 消费
     *
     * @throws Exception
     */
    public void myConsumer() throws Exception {
        String retValue;
        // 多线程环境的判断,一定要使用while进行,防止出现虚假唤醒
        // 当FLAG为true的时候,开始生产
        while (FLAGE) {
            // 取值 2秒有效期
            retValue = queue.poll(2L, TimeUnit.SECONDS);
            Thread.sleep(50);
            if (retValue != null && !retValue.equals("")) {
                System.out.println(Thread.currentThread().getName() + "\t 消费队列:" + retValue + "成功");
                System.out.println("");
            } else {
                FLAGE = false;
                System.out.println(Thread.currentThread().getName() + "\t 消费失败,队列中已为空,退出");
                System.out.println("");
                // 退出消费队列
                return;
            }
        }
    }

    public void stop() {
        FLAGE = false;
    }
}

public class ProdConsumerBlockingQueueDemo {
    public static void main(String[] args) {
        MySource mySource = new MySource(new ArrayBlockingQueue<>(10));

        new Thread(() -> {
            try {
                mySource.myProd();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }, "AAAA").start();


        new Thread(() -> {
            try {
                mySource.myConsumer();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }, "BBBB").start();

        try {
            Thread.sleep(10000);

        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("");
        System.out.println("10秒中后,生产和消费线程停止,线程结束");
        System.out.println();
        mySource.stop();
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值