java之阻塞队列BlockingQueue解析

 更新时间:2023年10月24日 10:18:11   作者:强强爱java  
这篇文章主要介绍了java之阻塞队列BlockingQueue解析,阻塞队列是一种线程安全的数据结构,用于在多线程环境下进行数据交换,它提供了一种阻塞的机制,当队列为空时,消费者线程将被阻塞,直到队列中有数据可供消费,需要的朋友可以参考下

一、阻塞队列概念

阻塞队列,顾名思义,首先它是一个队列(先进先出),而一个阻塞队列在数据结构所起到的作用大致如下图:

在这里插入图片描述

  • 线程1往阻塞队列中添加元素,而线程2从阻塞队列中移除元素
  • 当阻塞队列是空是,从队列中获取元素的操作会被阻塞
  • 当阻塞队列是满时,从队列中添加元素的操作会被阻塞
  • 试图从空的阻塞队列中获取元素的线程将会被阻塞,直到其他的线程往空的队列插入新的元素。
  • 试图网已满的阻塞队列中添加新元素的线程同样会被阻塞,直到其他的线程从列中移除一个或者多个元素或者完全清空队列后使队列重新变得空闲起来并后续新增

二、阻塞队列的好处

1.在多线程领域:所谓阻塞,在某些情况下会挂起线程,一旦满足条件,被挂起的线程又会自动被唤醒。

2.我们不需要关心什么时候需要阻塞线程,什么时候需要唤醒线程,因为这一切BlockingQueue(阻塞队列)都给你一手包办了

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

三、阻塞队列种类

  • ArrayBlockingQueue:由数据结构组成的有界阻塞队列
  • LinkedBlockingQueue:由链表结构组成的有界(但大小默认值为 Integer.MAX_VALUE )阻塞队列
  • PriorityBlockingQueue:支持优先级排序的无界阻塞队列
  • DelayQueue:使用优先级队列实现的延迟无界阻塞队列
  • SynchronousQueue:不存储元素的阻塞队列,也即单个元素的队列
  • LinkedTransferQueue:由链表结构组成的无界阻塞队列
  • LinkedBlockingDeque:由历览表结构组成的双向阻塞队列

PS:重点掌握ArrayBlockingQueue、LinkedBlockingQueue、SychronousQueue三种

四、BlockingQueue的核心方法

方法类型抛出异常特殊值一直阻塞超时退出
插入add(e)offer(e)put(e)offer(e,time,unit)
移除remove()poll()takepoll(time,unit)
检查element()peek()不可用不可用

说明:

方法类型情况
抛出异常当阻塞队列满时,再往队列中add会抛 IllegalStateException: Queue full;
当阻塞队列空时,再从队列里remove会抛 NoSuchElementException
特殊值offer(e)插入方法,成功true失败false
poll() 移除方法,成功返回出队列的元素,队列里没有就返回null
一直阻塞当阻塞队列满时,生产者线程继续往队列里put元素,队列会一直阻塞线程直到take数据或响应中断退出
当阻塞队列空时,消费者线程试图从队列take元素,队列会一直阻塞消费者线程直到队列可用
超时退出当阻塞队列满时,队列会阻塞生产者线程一定时间,超过限时后生产者线程会退出

五、示例代码

1.使用SychronousQueue队列

package com.jian8.juc.queue;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;

/*
 * 阻塞队列SynchronousQueue演示
 * */
public class SynchronousQueueDemo {
    public static void main(String[] args) {
        BlockingQueue<String> blockingQueue = new SynchronousQueue<>();

        new Thread(() -> {
            try {
                System.out.println(Thread.currentThread().getName() + "\t put 1");
                blockingQueue.put("1");
                System.out.println(Thread.currentThread().getName() + "\t put 2");
                blockingQueue.put("2");
                System.out.println(Thread.currentThread().getName() + "\t put 3");
                blockingQueue.put("3");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "AAA").start();

        new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(2);
                System.out.println(Thread.currentThread().getName() + "\t take" + blockingQueue.take());

                TimeUnit.SECONDS.sleep(2);
                System.out.println(Thread.currentThread().getName() + "\t take" + blockingQueue.take());

                TimeUnit.SECONDS.sleep(2);
                System.out.println(Thread.currentThread().getName() + "\t take" + blockingQueue.take());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "BBB").start();
    }
}

运行结果:

在这里插入图片描述

2.传统版生产者消费者模式

package com.jian8.juc.queue;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 一个初始值为零的变量,两个线程对其交替操作,一个加1一个减1,来5轮
 * 1. 线程  操作  资源类
 * 2. 判断  干活  通知
 * 3. 防止虚假唤起机制
 */
public class ProdConsumer_TraditionDemo {
    public static void main(String[] args) {
        ShareData shareData = new ShareData();
        for (int i = 1; i <= 5; i++) {
            new Thread(() -> {
                try {
                    shareData.increment();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }, "ProductorA " + i).start();
        }

        for (int i = 1; i <= 5; i++) {
            new Thread(() -> {
                try {
                    shareData.decrement();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }, "ConsumerA  " + i).start();
        }

        for (int i = 1; i <= 5; i++) {
            new Thread(() -> {
                try {
                    shareData.increment();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }, "ProductorB " + i).start();
        }

        for (int i = 1; i <= 5; i++) {
            new Thread(() -> {
                try {
                    shareData.decrement();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }, "ConsumerB  " + i).start();
        }

    }
}

//资源类
class ShareData {
    private int number = 0;
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    //生产
    public void increment() throws Exception {
        lock.lock();
        try {
            //1.判断
            while (number != 0) {
                //等待中,不能生产
                condition.await();
            }
            //2.干活
            number++;
            System.out.println(Thread.currentThread().getName() + "\t" + number);
            //3.通知
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    //消费
    public void decrement() throws Exception {
        lock.lock();
        try {
            //1.判断
            while (number == 0) {
                //等待中,不能消费
                condition.await();
            }
            //2.消费
            number--;
            System.out.println(Thread.currentThread().getName() + "\t" + number);
            //3.通知
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

}

运行结果(部分):

在这里插入图片描述

3.阻塞队列版(以ArrayBlockingQueue为例)生产者消费者模式

package com.jian8.juc.queue;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class ProdConsumer_BlockQueueDemo {
    public static void main(String[] args) {
        MyResource myResource = new MyResource(new ArrayBlockingQueue<>(10));

        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "\t生产线程启动");
            try {
                myResource.myProd();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }, "Prod").start();

        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "\t消费线程启动");
            try {
                myResource.myConsumer();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }, "Consumer").start();

        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("5s后main叫停,线程结束");

        try {
            myResource.stop();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

//资源类
class MyResource {
    private volatile boolean flag = true;//默认开启,进行“生产+消费”活动
    private AtomicInteger atomicInteger = new AtomicInteger();
    BlockingQueue<String> blockingQueue = null;//消息队列

    public MyResource(BlockingQueue<String> blockingQueue) {
        this.blockingQueue = blockingQueue;
        System.out.println(blockingQueue.getClass().getName());
    }

    //生产
    public void myProd() throws Exception {
        String data = null;
        boolean retValue;
        while (flag) {
            data = atomicInteger.incrementAndGet() + "";
            //超过2s没生产成功,退出生产
            retValue = blockingQueue.offer(data, 2, TimeUnit.SECONDS);
            if (retValue) {
                System.out.println(Thread.currentThread().getName() + "\t插入队列" + data + "成功");
            } else {
                System.out.println(Thread.currentThread().getName() + "\t插入队列" + data + "失败");
            }
            TimeUnit.SECONDS.sleep(1);
        }
        System.out.println(Thread.currentThread().getName() + "\t大老板叫停了,flag=false,生产结束");
    }

    //消费
    public void myConsumer() throws Exception {
        String result = null;
        while (flag) {
            //超过2s没从队列获取数据,消费退出
            result = blockingQueue.poll(2, TimeUnit.SECONDS);
            if (null == result || result.equalsIgnoreCase("")) {
                flag = false;
                System.out.println(Thread.currentThread().getName() + "\t超过2s没有取到蛋糕,消费退出");
                System.out.println();
                return;
            }
            System.out.println(Thread.currentThread().getName() + "\t消费队列" + result + "成功");
        }
    }

    public void stop() throws Exception {
        flag = false;
    }
}

运行结果:

在这里插入图片描述

到此这篇关于java之阻塞队列BlockingQueue解析的文章就介绍到这了,更多相关java阻塞队列BlockingQueue内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Springboot如何实现代理服务器

    Springboot如何实现代理服务器

    这篇文章主要介绍了Springboot如何实现代理服务器问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-06-06
  • Java 静态机制之静态变量、方法、代码块与类加载深入理解

    Java 静态机制之静态变量、方法、代码块与类加载深入理解

    本文全面解析了Java中static关键字的特性与应用,静态机制使成员与类本身关联,包括静态变量(存储在方法区,所有对象共享)、静态方法(可通过类名直接调用,不能访问实例成员)和静态代码块(类加载时执行),感兴趣的朋友跟随小编一起看看吧
    2025-11-11
  • 深入理解Java设计模式之命令模式

    深入理解Java设计模式之命令模式

    这篇文章主要介绍了JAVA设计模式之命令模式的的相关资料,文中示例代码非常详细,供大家参考和学习,感兴趣的朋友可以了解
    2021-11-11
  • 深入学习java8 中的CompletableFuture

    深入学习java8 中的CompletableFuture

    本文主要介绍了java8中的CompletableFuture,CompletableFuture实现了CompletionStage接口和Future接口,前者是对后者的一个扩展,增加了异步回调、流式处理、多个Future组合处理的能力,使Java在处理多任务的协同工作时更加顺畅便利,下文需要的朋友可以参考一下
    2022-05-05
  • 如何实现Spring Event(异步事件)

    如何实现Spring Event(异步事件)

    这篇文章主要介绍了如何实现Spring Event(异步事件)问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-02-02
  • SpringBoot自定义/error路径失效的解决

    SpringBoot自定义/error路径失效的解决

    这篇文章主要介绍了SpringBoot自定义/error路径失效的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-01-01
  • Ubuntu16.04安装部署solr7的图文详细教程

    Ubuntu16.04安装部署solr7的图文详细教程

    这篇文章主要为大家详细介绍了Ubuntu16.04安装部署solr7的图文详细教程,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-07-07
  • Spring Security和Shiro的相同点与不同点整理

    Spring Security和Shiro的相同点与不同点整理

    在本篇文章里小编给大家整理的是关于Spring Security和Shiro的相同不同点整理,需要的朋友们可以参考下。
    2020-02-02
  • Java多线程中的concurrent简析

    Java多线程中的concurrent简析

    这篇文章主要介绍了Java多线程中的concurrent简析,java.util.concurrent包提供了很多有用的类,方便我们进行并发程序的开发,本文将会挑选其中常用的一些类来进行大概的说明,需要的朋友可以参考下
    2023-09-09
  • SpringBoot详细讲解yaml配置文件的用法

    SpringBoot详细讲解yaml配置文件的用法

    这篇文章主要介绍了SpringBoot中的yaml配置文件问题,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-06-06

最新评论