Java数据结构12-死磕Java队列-有界阻塞

7 篇文章 0 订阅
5 篇文章 0 订阅

1. 背景

在 [[Java数据结构11-死磕Java队列-LinkedList]] 文章中,我们看到 LinkedList 在获取元素或者插入元素过程中,因为队列是非阻塞,所以不对对应用进行阻断,这在很多时候,并不能满足我们的实际要求。当然这种是否需要阻塞也是看实际情况而言,就其效率和实际应用设计程度相对有界非阻塞而言稍微复杂,因阻塞而挂起的场景需要额外做适配,防止线程长时间被挂起而达不到释放,造成资源过渡开销。

2. 适用场景

当我们在生产或者消费队列中的元素,如果我们获取元素过程中队列无元素或者写入队列时候,队列的容量已经满了,此时不论是消费者线程还是生产者线程都应该被阻塞。直到队列中有元素或者队列容量非最大。

3. 实例

有界阻塞队列常用的主要有 ArrayBlockingQueueLinkedBlockingQueue ,他们实现了 BlockingQueue 阻塞队列接口,并在基础上位置一个有容量上限的集合。

3.1. ArrayBlockingQueue

ArrayBlockingQueue 在内部维持一个 Object[] 类型的数组 。

3.1.1. 构造函数
  • ArrayBlockingQueue(int capacity):指定容量上限,并且默认的访问顺序
  • ArrayBlockingQueue(int capacity, boolean fair):指定容量上限,并且可指定所需要访问是否按照顺序
  • ArrayBlockingQueue(int capacity, boolean fair,Collection<? extends E> c):指定容量上限,并且可指定所需要访问是否按照顺序,以及指定元素的集合

20221003120036

3.1.2. 常用方法
方法名描述
add将指定的元素添加为队列尾部,如果队列已满,则抛出异常
offer将指定的元素添加为队列尾部,如果队列已满,则返回 False
put将指定的元素插入队列尾部,如果队列已满,则等待空间的出现。
size返回队列当前的容量大小
remove从这个队列中删除一个指定元素的单个实例
poll检索并删除此列表的头(第一个元素)
pop从此列表表示的堆栈中弹出一个元素,换句话说,删除并返回此列表的第一个元素
peek检索但不删除此列表的头(第一个元素)
take检索并删除此列表的头(第一个元素),没有元素的过程中,线程会等待
3.1.3. 样例

队列容量大小为 10 ,三个生产者线程和三个消费者线程,生产者线程每次朝队列中添加 5 个元素,消费者线程每次从队列头部消费 5 个元素。

在生产者添加过程中,如果队列已满,则生产者队列阻塞等待,最后,消费者线程如果遇到队列中没有元素,那么消费者线程将一直阻塞等待。

  • ArrayBlockingQueueDemo:数组队列核心实例方法

public class ArrayBlockingQueueDemo {

    public static void main(String[] args) {
        BlockingQueue<String> queue = new ArrayBlockingQueue<>(10);
        BlockingQueueUtil util = new BlockingQueueUtil(queue);

        ExecutorService exe= ThreadPoolsUtil.doCreate(2,10,"BLOCKING");

        for (int i = 0; i < 3; i++) {
            exe.submit(new ProducerCase(util));
        }

        for (int i = 0; i < 3; i++) {
            exe.submit(new ConsumberCase(util));
        }

        exe.shutdown();
    }
}


  • ConsumberCase:消费者

class ConsumberCase implements Runnable {

    private BlockingQueueUtil util;

    ConsumberCase(BlockingQueueUtil util) {
        this.util = util;
    }

    @SneakyThrows
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            util.romove();
        }
    }
}


  • ProducerCase:生产者

class ProducerCase implements Runnable {
    private BlockingQueueUtil util;

    ProducerCase(BlockingQueueUtil util) {
        this.util = util;
    }

    @SneakyThrows
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            String val = "Qu_" + i;
            if (!util.add(val)) {
                System.out.println(Thread.currentThread().getName() + "; [Value ]: " + val + " ;队列已满,需要等待");
            }
        }
    }
}

  • BlockingQueueUtil:队列工具类

public class BlockingQueueUtil {

    BlockingQueue<String> queue;

    BlockingQueueUtil(BlockingQueue<String> queue) {
        this.queue = queue;
    }

    public boolean add(String val) {
        boolean flag = queue.offer(val);
        System.out.println(Thread.currentThread().getName() +"; [Value ]: "+ val+ " ;添加后队列容量大小为:" + queue.size());
        return flag;
    }

    /**
     * 没有元素,线程则等待
     *
     * @param
     * @return String
     * @author <a href="https://github.com/rothschil">Sam</a>
     **/
    public String romove() throws InterruptedException {
        String val = queue.take();
        System.out.println(Thread.currentThread().getName() +"; [Value ]: "+ val+" ;剩余容量大小为:" + queue.size());
        return val;
    }
}


3.1.4. 日志

BLOCKING-1-thread-2; [Value ]: Qu_0 ;添加后队列容量大小为:2
BLOCKING-1-thread-2; [Value ]: Qu_1 ;添加后队列容量大小为:3
BLOCKING-1-thread-1; [Value ]: Qu_0 ;添加后队列容量大小为:2
BLOCKING-1-thread-2; [Value ]: Qu_2 ;添加后队列容量大小为:4
BLOCKING-1-thread-1; [Value ]: Qu_1 ;添加后队列容量大小为:5
BLOCKING-1-thread-2; [Value ]: Qu_3 ;添加后队列容量大小为:6
BLOCKING-1-thread-1; [Value ]: Qu_2 ;添加后队列容量大小为:7
BLOCKING-1-thread-2; [Value ]: Qu_4 ;添加后队列容量大小为:8
BLOCKING-1-thread-1; [Value ]: Qu_3 ;添加后队列容量大小为:9
BLOCKING-1-thread-1; [Value ]: Qu_4 ;添加后队列容量大小为:10
BLOCKING-1-thread-2; [Value ]: Qu_0 ;添加后队列容量大小为:10
BLOCKING-1-thread-1; [Value ]: Qu_0 ;剩余容量大小为:9
BLOCKING-1-thread-2; [Value ]: Qu_0 ;队列已满,需要等待
BLOCKING-1-thread-1; [Value ]: Qu_0 ;剩余容量大小为:8
BLOCKING-1-thread-2; [Value ]: Qu_1 ;添加后队列容量大小为:9
BLOCKING-1-thread-1; [Value ]: Qu_1 ;剩余容量大小为:8
BLOCKING-1-thread-2; [Value ]: Qu_2 ;添加后队列容量大小为:9
BLOCKING-1-thread-2; [Value ]: Qu_3 ;添加后队列容量大小为:9
BLOCKING-1-thread-1; [Value ]: Qu_2 ;剩余容量大小为:8
BLOCKING-1-thread-2; [Value ]: Qu_4 ;添加后队列容量大小为:10
BLOCKING-1-thread-1; [Value ]: Qu_1 ;剩余容量大小为:9
BLOCKING-1-thread-2; [Value ]: Qu_3 ;剩余容量大小为:8
BLOCKING-1-thread-1; [Value ]: Qu_2 ;剩余容量大小为:7
BLOCKING-1-thread-2; [Value ]: Qu_4 ;剩余容量大小为:6
BLOCKING-1-thread-1; [Value ]: Qu_3 ;剩余容量大小为:5
BLOCKING-1-thread-2; [Value ]: Qu_4 ;剩余容量大小为:4
BLOCKING-1-thread-1; [Value ]: Qu_1 ;剩余容量大小为:3
BLOCKING-1-thread-2; [Value ]: Qu_2 ;剩余容量大小为:2
BLOCKING-1-thread-1; [Value ]: Qu_3 ;剩余容量大小为:1
BLOCKING-1-thread-2; [Value ]: Qu_4 ;剩余容量大小为:0


3.2. LinkedBlockingQueue

LinkedBlockingQueue 在内部维持一个链表。

3.2.1. 构造函数
  • LinkedBlockingQueue():这是创造一个无界的队列,容量上限就是 Integer.MAX_VALUE <不推荐使用>
  • LinkedBlockingQueue(int capacity):创建一个有容量上限的队列
  • LinkedBlockingQueue(Collection<? extends E> c):根据给定的集合,创建队列

20221003120150

3.2.2. 常用方法
方法名描述
offer将指定的元素添加为队列尾部,如果队列已满,则返回 False
put将指定的元素插入队列尾部,如果队列已满,则等待空间的出现。
size返回队列当前的容量大小
remove从这个队列中删除一个指定元素的单个实例
poll检索并删除此列表的头(第一个元素)
peek检索但不删除此列表的头(第一个元素)
take检索并删除此列表的头(第一个元素),没有元素的过程中,线程会等待
iterator以适当的顺序返回这个队列中的元素的迭代器
dequeue将一个节点从队列的头部移除
3.2.3. 样例

public class LinkedBlockingQueueDemo {

    public static void main(String[] args) {
        BlockingQueue<String> queue = new LinkedBlockingQueue<>(10);
        BlockingQueueUtil util = new BlockingQueueUtil(queue);
        ExecutorService exe= ThreadPoolsUtil.doCreate(2,10,"BLOCKING");
        for (int i = 0; i < 3; i++) {
            exe.submit(new ProducerCase(util));
        }
        for (int i = 0; i < 3; i++) {
            exe.submit(new ConsumberCase(util));
        }

        exe.shutdown();
    }
}


3.2.4. 日志

BLOCKING-1-thread-1; [Value ]: Qu_0 ;添加后队列容量大小为:1
BLOCKING-1-thread-2; [Value ]: Qu_0 ;添加后队列容量大小为:2
BLOCKING-1-thread-1; [Value ]: Qu_1 ;添加后队列容量大小为:3
BLOCKING-1-thread-2; [Value ]: Qu_1 ;添加后队列容量大小为:4
BLOCKING-1-thread-2; [Value ]: Qu_2 ;添加后队列容量大小为:6
BLOCKING-1-thread-1; [Value ]: Qu_2 ;添加后队列容量大小为:5
BLOCKING-1-thread-2; [Value ]: Qu_3 ;添加后队列容量大小为:7
BLOCKING-1-thread-2; [Value ]: Qu_4 ;添加后队列容量大小为:9
BLOCKING-1-thread-1; [Value ]: Qu_3 ;添加后队列容量大小为:8
BLOCKING-1-thread-1; [Value ]: Qu_4 ;添加后队列容量大小为:10
BLOCKING-1-thread-1; [Value ]: Qu_0 ;添加后队列容量大小为:10
BLOCKING-1-thread-1; [Value ]: Qu_0 ;队列已满,需要等待
BLOCKING-1-thread-1; [Value ]: Qu_1 ;添加后队列容量大小为:10
BLOCKING-1-thread-1; [Value ]: Qu_1 ;队列已满,需要等待
BLOCKING-1-thread-1; [Value ]: Qu_2 ;添加后队列容量大小为:10
BLOCKING-1-thread-1; [Value ]: Qu_3 ;添加后队列容量大小为:10
BLOCKING-1-thread-1; [Value ]: Qu_3 ;队列已满,需要等待
BLOCKING-1-thread-1; [Value ]: Qu_4 ;添加后队列容量大小为:10
BLOCKING-1-thread-2; [Value ]: Qu_0 ;剩余容量大小为:9
BLOCKING-1-thread-1; [Value ]: Qu_4 ;队列已满,需要等待
BLOCKING-1-thread-1; [Value ]: Qu_1 ;剩余容量大小为:8
BLOCKING-1-thread-2; [Value ]: Qu_0 ;剩余容量大小为:9
BLOCKING-1-thread-1; [Value ]: Qu_1 ;剩余容量大小为:7
BLOCKING-1-thread-2; [Value ]: Qu_2 ;剩余容量大小为:6
BLOCKING-1-thread-2; [Value ]: Qu_2 ;剩余容量大小为:5
BLOCKING-1-thread-1; [Value ]: Qu_3 ;剩余容量大小为:4
BLOCKING-1-thread-2; [Value ]: Qu_3 ;剩余容量大小为:3
BLOCKING-1-thread-1; [Value ]: Qu_4 ;剩余容量大小为:2
BLOCKING-1-thread-2; [Value ]: Qu_4 ;剩余容量大小为:1
BLOCKING-1-thread-1; [Value ]: Qu_2 ;剩余容量大小为:0

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

王老邪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值