并发编程(三)-阻塞队列

1.阻塞队列概念

BlockingQueue,是java.util.concurrent 包提供的用于解决并发生产者 - 消费者问题的最有用的类,它的特性是在任意时刻只有一个线程可以进行take或者put操作,并且BlockingQueue提供了超时return null的机制,在许多生产场景里都可以看到这个工具的身影。常见的四种阻队列

ArrayBlockingQueue //由数组支持的有界队列
LinkedBlockingQueue //由链接节点支持的可选有界队列
PriorityBlockingQueue //由优先级堆支持的无界优先级队列
DelayQueue// 由优先级堆支持的、基于时间的调度队列

2.四种阻塞队列详解

ArrayBlockingQueue:
在这里插入图片描述
LinkedBlockingQueue:
向无限队列添加元素的所有操作都将永远不会阻塞,[注意这里不是说不会加锁保证线程安全],因此它可以增长到非常大的容量。
使用无限 BlockingQueue 设计生产者 - 消费者模型时最重要的是 消费者应该能够像生产者向队列添加消息一样快地消费消息 。否则,内存可能会填满,然后就会得到一个 OutOfMemory 异常。
DelayQueue:
在这里插入图片描述

源码解释(生产者消费者模型)

示例代码:

@Slf4j
public class NumbersProducer implements Runnable {
    private BlockingQueue<Integer> numbersQueue;
    private final int poisonPill;
    private final int poisonPillPerProducer;

    public NumbersProducer(BlockingQueue<Integer> numbersQueue, int poisonPill, int poisonPillPerProducer) {
        this.numbersQueue = numbersQueue;
        this.poisonPill = poisonPill;
        this.poisonPillPerProducer = poisonPillPerProducer;
    }
    public void run() {
        try {
            generateNumbers();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }

    private void generateNumbers() throws InterruptedException {
        for (int i = 0; i < 100; i++) {
            numbersQueue.put(ThreadLocalRandom.current().nextInt(100));
            log.info("潘金莲-{}号,给武大郎的泡药!",Thread.currentThread().getId());
        }
        for (int j = 0; j < poisonPillPerProducer; j++) {
            numbersQueue.put(poisonPill);
            log.info("潘金莲-{}号,往武大郎的药里放入第{}颗毒丸!",Thread.currentThread().getId(),j+1);
        }
    }
}
@Slf4j
public class NumbersConsumer implements Runnable {
    private BlockingQueue<Integer> queue;
    private final int poisonPill;

    public NumbersConsumer(BlockingQueue<Integer> queue, int poisonPill) {
        this.queue = queue;
        this.poisonPill = poisonPill;
    }

    public void run() {
        try {
            while (true) {
                Integer number = queue.take();
                if (number.equals(poisonPill)) {
                    return;
                }
                log.info("武大郎-{}号,喝药-编号:{}",Thread.currentThread().getId(),number);
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}
public class Main {

    public static void main(String[] args) {
        int BOUND = 10;
        int N_PRODUCERS = 16;
        int N_CONSUMERS = Runtime.getRuntime().availableProcessors();
        int poisonPill = Integer.MAX_VALUE;
        int poisonPillPerProducer = N_CONSUMERS / N_PRODUCERS;
        int mod = N_CONSUMERS % N_PRODUCERS;

        BlockingQueue<Integer> queue = new LinkedBlockingQueue<>(BOUND);
        //潘金莲给武大郎熬药
        for (int i = 1; i < N_PRODUCERS; i++) {
            new Thread(new NumbersProducer(queue, poisonPill, poisonPillPerProducer)).start();
        }
        //武大郎开始喝药
        for (int j = 0; j < N_CONSUMERS; j++) {
            new Thread(new NumbersConsumer(queue, poisonPill)).start();
        }
        //潘金莲开始投毒,武大郎喝完毒药GG
        new Thread(new NumbersProducer(queue, poisonPill, poisonPillPerProducer + mod)).start();
    }

}

生产者们和消费们同时竞争队列的使用权(加锁)
生产者向队列中添加元素
在这里插入图片描述
首先AQS加锁,如果当前的入队的元素已经达到了阻塞队列的边界,则该生产者(线程)进入条件等在状态,等到队列不满的时候继续入队
在这里插入图片描述
notfull.wait()
进入条件队列后,判断线程是否存在中断,抛出异常。park唤醒另一个条件等待队列中的结点。
在这里插入图片描述
消费者消费
在这里插入图片描述
和生产者类似,先加锁,队列为空时,停止消费,进入条件阻塞队列,
在这里插入图片描述
同时唤醒其他条件阻塞队列中的线程
在这里插入图片描述
附图解释:
在这里插入图片描述
如存在不正确之处,欢迎各位大佬指正

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 《Java并发编程实战》是一本经典的Java并发编程指南,由Brian Goetz等人撰写。这本书针对Java多线程并发编程的实践问题给出了详细的解决方案和最佳实践。 该书的主要内容包括:线程安全性、对象的共享与发布、锁的优化、性能与可伸缩性、构建和组合对象、基础构建模块、任务执行、取消与关闭、线程池的使用、显式锁、构建自定义的同步工具等。 通过阅读这本书,读者可以了解Java中的各种并发问题,并学习如何设计和实现线程安全的Java应用。该书引入了很多并发编程的常见问题,例如:竞态条件、死锁、活跃性危险等,并提供了一些模式和技术来解决这些问题。 除了基础知识之外,该书还介绍了一些高级的并发编程概念,例如:并发集合、同步类、线程池等。这些内容将帮助读者更好地理解和利用Java并发编程能力。 总的来说,《Java并发编程实战》是一本权威而实用的Java并发编程指南,适合对多线程编程有一定了解的Java开发人员阅读。通过阅读这本书,读者可以更深入地理解Java并发编程的原理与应用,提高自己的并发编程能力。 ### 回答2: 《Java并发编程实战》是由美国计算机科学家布莱恩·戈策等人合著的一本书,是学习Java并发编程的经典教材。这本书系统地介绍了Java中的多线程、并发和并行编程的基本知识和应用。该书分为个部分,分别是基础篇、高级主题篇和专题扩展篇。 在基础篇中,书中详细介绍了Java内存模型、线程安全性、对象的共享、发布和逸出等概念。同时,还对Java中的锁、线程池、阻塞队列等常用并发工具进行了深入讲解,帮助读者理解并发编程的基本原理和机制。 高级主题篇则讨论了一些更加复杂的并发编程问题,如线程间的协作、线程间通信、并发集合类的使用和自定义的同步工具的设计等内容。该篇章通过讲解常见的并发问题和解决方案,提供了应对复杂并发场景的实践经验。 专题扩展篇主要讨论了一些与并发编程相关的主题,如并发性问题的调试与测试、程序性能调优等。这些内容能够帮助读者进一步提升对并发编程的理解和应用水平。 《Java并发编程实战》通过深入浅出的语言和大量实例,帮助读者掌握并发编程的基本概念和技术,为读者提供设计和编写高效多线程程序的实践经验。无论是Java初学者还是有一定经验的开发人员,都可以从中获得丰富的知识和实用的技巧,加速自己在并发编程领域的成长。 ### 回答3: 《Java并发编程实战》是一本经典的Java多线程编程指南,被广泛认可为学习并发编程的必读之作。本书由Brian Goetz等多位并发编程领域的专家合著,内容全面且深入浅出,适合从初学者到高级开发人员阅读。 该书分为四个部分,共16章。第一部分介绍了并发编程的基础知识,包括线程安全性、对象的共享、对象组合等。第二部分讲解了如何构建可复用的并发构件,包括线程安全性、发布与初始化安全性等。第部分深入讨论了Java并发编程中常见的问题和挑战,例如活跃性、性能与可伸缩性等。第四部分则介绍了一些高级主题,如显式锁、原子变量和并发集合等。 书中包含了大量的示例代码和实践案例,可以帮助读者更好地理解并发编程的概念和技术。此外,本书还提供了一些最佳实践和经验教训,帮助读者避免常见的并发编程陷阱和错误。 《Java并发编程实战》从理论到实践的结合非常好,书中所介绍的内容都是经过实践验证的,具有很高的可靠性和实用性。无论是初学者还是有一定经验的开发人员,都可以从中获得实际应用的知识和经验。 综上所述,如果你想系统地学习Java并发编程,了解如何编写高效、可靠的多线程代码,那么《Java并发编程实战》是一本值得推荐的书籍。它可以帮助你深入理解并发编程的原理和技术,提高自己在并发编程领域的能力和水平。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值