【Java并发编程学习 6】并发队列学习

并发队列

1 什么是并发队列:
  1. 消息队列很多人知道:消息队列是分布式系统中重要的组件,是系统与系统直接的通信
  2. 并发队列是什么:并发队列多个线程以有次序共享数据的重要组件

2 并发队列和并发集合的区别:

那就有可能要说了,我们并发集合不是也可以实现多线程之间的数据共享吗,其实也是有区别的:

  1. 队列遵循“先进先出”的规则,可以想象成排队检票,队列一般用来解决大数据量采集处理和显示的。
  2. 并发集合就是在多个线程中共享数据的

2 并发队列介绍

在并发队列上JDK提供了Queue接口,一个是以Queue接口下的BlockingQueue接口为代表的阻塞队列,另一个是高性能(无堵塞)队列。

3 阻塞队列和非阻塞队列区别
  1. 当队列阻塞队列为空的时,从队列中获取元素的操作将会被阻塞。
  2. 或者当阻塞队列是满时,往队列里添加元素的操作会被阻塞。
  3. 或者试图从空的阻塞队列中获取元素的线程将会被阻塞,直到其他的线程往空的队列插入新的元素。
  4. 试图往已满的阻塞队列中添加新元素的线程同样也会被阻塞,直到其他的线程使队列重新变得空闲起来

4 Java常用的并发队列:

其实判断是否是堵塞队列很简单,只要继承了BlockingQueue的都是堵塞队列

4.1 JDK11的Queue子父关系:

在这里插入图片描述
AbstractQueue(翻译:抽象队列):实现了AbstractQueue的不一定是堵塞或者非堵塞列队
BlockingQueue(翻译:堵塞队列):实现了BlockingQueue接口的一定是堵塞列队

5 常用并发列队的介绍:
  1. 非堵塞队列:
    1.ArrayDeque, (数组双端队列)
    2.PriorityQueue, (优先级队列)
    3.ConcurrentLinkedQueue, (基于链表的并发队列)
  2. 堵塞队列:
    1.DelayQueue, (基于时间优先级的队列,延期阻塞队列)
    2.ArrayBlockingQueue, (基于数组的并发阻塞队列)
    3.LinkedBlockingQueue, (基于链表的FIFO阻塞队列)
    4.LinkedBlockingDeque, (基于链表的FIFO双端阻塞队列)
    5.PriorityBlockingQueue, (带优先级的无界阻塞队列)
    6.SynchronousQueue (并发同步阻塞队列)

6 常用非堵塞队列的使用:
6.1 ArrayDeque

ArrayDeque (非堵塞队列)是JDK容器中的一个双端队列实现,内部使用数组进行元素存储,不允许存储null值,可以高效的进行元素查找和尾部插入取出,是用作队列、双端队列、栈的绝佳选择,性能比LinkedList还要好。

ArrayDeque的重要方法:

add(E e) 在队列尾部添加一个元素
offer(E e) 在队列尾部添加一个元素,并返回是否成功
remove() 删除队列中第一个元素,并返回该元素的值,如果元素为null,将抛出异常(其实底层调用的是removeFirst())
poll() 删除队列中第一个元素,并返回该元素的值,如果元素为null,将返回null(其实调用的是pollFirst())
element() 获取第一个元素,如果没有将抛出异常
peek() 获取第一个元素,如果返回null

ArrayDeque代码示例:
package com.lijie;

import java.util.ArrayDeque;

public class Test01 {
    public static void main(String[] args) {
        ArrayDeque q = new ArrayDeque();
        q.add("张三");
        q.offer("李四");
        q.offer("小红");

        //从头获取元素,删除该元素
        System.out.println(q.poll());
        //从头获取元素,不刪除该元素
        System.out.println(q.peek());
        //从头获取元素,不刪除该元素
        System.out.println(q.peek());
        //获取总长度
        System.out.println(q.size());
    }
}
6.2 ConcurrentLinkedQueue

ConcurrentLinkedQueue (非堵塞队列): 是一个适用于高并发场景下的队列,通过无锁的方式,实现了高并发状态下的高性能。ConcurrentLinkedQueue的性能要好于BlockingQueue接口,它是一个基于链接节点的无界线程安全队列。该队列的元素遵循先进先出的原则。该队列不允许null元素。

ConcurrentLinkedQueue重要方法:

add() 和offer() 都是是添加元素方法
poll() 是提取元素,提取完删除元素方法
peek() 是提取元素,提取完不删除方法

ConcurrentLinkedQueue代码示例:
package com.lijie;

import java.util.concurrent.ConcurrentLinkedDeque;

public class Test02 {
    public static void main(String[] args) {
        ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
        q.add("张三");
        q.offer("李四");
        q.offer("小红");
        //从头获取元素,删除该元素
        System.out.println(q.poll());
        //从头获取元素,不刪除该元素
        System.out.println(q.peek());
        //从头获取元素,不刪除该元素
        System.out.println(q.peek());
        //获取总长度
        System.out.println(q.size());
    }
}
6.3 PriorityQueue

PriorityQueue (非堵塞队列) 一个基于优先级的无界优先级队列。优先级队列的元素按照其自然顺序进行排序,或者根据构造队列时提供的 Comparator 进行排序,具体取决于所使用的构造方法。该队列不允许使用 null 元素也不允许插入不可比较的对象

PriorityQueue重要方法:

peek()//返回队首元素
poll()//返回队首元素,队首元素出队列
add()//添加元素
size()//返回队列元素个数
isEmpty()//判断队列是否为空,为空返回true,不空返回false

PriorityQueue代码示例:
package com.lijie;


import java.util.PriorityQueue;

public class Test03 {
    public static void main(String[] args) {
        PriorityQueue<String> q = new PriorityQueue<String>();
        //入列
        q.offer("1");
        q.offer("5");
        q.offer("2");
        q.offer("3");
        q.offer("4");

        //出列
        System.out.println(q.poll());  // 1
        System.out.println(q.poll());  // 2
        System.out.println(q.poll());  // 3
        System.out.println(q.poll());  // 4
        System.out.println(q.poll());  // 5
    }
}

观察打印结果, 入列:15234, 出列是12345, 也是说出列时他自己做了相关的判断

PriorityQueue自定义代码比较器示例:
package com.lijie;


import java.util.Comparator;
import java.util.PriorityQueue;

public class Test04 {
    //自定义比较器,降序排列
    static Comparator<Integer> cmp = new Comparator<Integer>() {
        public int compare(Integer e1, Integer e2) {
            return e2 - e1;
        }
    };

    public static void main(String[] args) {
        //构造参数添加比较器
        PriorityQueue<Integer> q = new PriorityQueue<>(cmp);
        //入列
        q.offer(1);
        q.offer(2);
        q.offer(3);
        q.offer(4);
        q.offer(5);

        //出列
        System.out.println(q.poll());  // 5
        System.out.println(q.poll());  // 4
        System.out.println(q.poll());  // 3
        System.out.println(q.poll());  // 2
        System.out.println(q.poll());  // 1
    }
}

观察打印结果, 入列:12345, 出列是54321, 也是说出列时他自己做了相关的判断

7 常用阻塞列队的使用
7.1 ArrayBlockingQueue

ArrayBlockingQueue是一个有边界的阻塞队列,它的内部实现是一个数组。有边界的意思是它的容量是有限的,我们必须在其初始化的时候指定它的容量大小,容量大小一旦指定就不可改变。ArrayBlockingQueue是以先进先出的方式存储数据

ArrayBlockingQueue重要方法:

add(): 在不超出队列长度的情况下插入元素,可以立即执行,成功返回true,如果队列满了就抛出异常。
offer(): 在不超出队列长度的情况下插入元素的时候则可以立即在队列的尾部插入指定元素,成功时返回true,如果此队列已满,则返回false。
put(): 插入元素的时候,如果队列满了就进行等待,直到队列可用。

remove():底层是用到了poll()方法,检索并且删除返回队列头的元素,与poll()方法不同的是,元素没有是进行抛异常NoSuchElementException。
poll(): 检索并且删除返回队列头的元素,有就返回没有就返回null。
take(): 检索并且删除返回队列头的元素,如果元素没有会一直等待,有就返回。
peek(): 检索但不移除此队列的头部;如果此队列为空,则返回null。返回头部元素。

ArrayBlockingQueue代码示例:
package com.lijie;

import java.util.concurrent.ArrayBlockingQueue;

public class Test05 {
    public static void main(String[] args){
        ArrayBlockingQueue<String> arrays = new ArrayBlockingQueue<String>(3);
        arrays.add("李四");
        arrays.add("张三");
        arrays.add("王五");
        arrays.add("小红");
        arrays.add("校长");
        //会抱错,在未消费之前不能添加列队了,列队已经排满,
    }
}
7.2 LinkedBlockingQueue

LinkedBlockingQueue阻塞队列大小的配置是可选的,如果我们初始化时指定一个大小,它就是有边界的,如果不指定,它就是无边界的。说是无边界,其实是采用了默认大小为Integer.MAX_VALUE的容量 。它的内部实现是一个链表。

和ArrayBlockingQueue一样,LinkedBlockingQueue 也是以先进先出的方式存储数据。

LinkedBlockingQueue的重要方法:

add(): 在不超出队列长度的情况下插入元素,可以立即执行,成功返回true,如果队列满了就抛出异常。
offer(): 在不超出队列长度的情况下插入元素的时候则可以立即在队列的尾部插入指定元素,成功时返回true,如果此队列已满,则返回false。
put(): 插入元素的时候,如果队列满了就进行等待,直到队列可用。
take(): 从队列中获取值,如果队列中没有值,线程会一直阻塞,直到队列中有值,并且该方法取得了该值。
poll(long timeout, TimeUnit unit): 在给定的时间里,从队列中获取值,如果没有取到会抛出异常。
remainingCapacity():获取队列中剩余的空间。
remove(Object o): 从队列中移除指定的值。
contains(Object o): 判断队列中是否拥有该值。
drainTo(Collection c): 将队列中值,全部移除,并发设置到给定的集合中。

LinkedBlockingQueue代码示例:
package com.lijie;

import java.util.concurrent.LinkedBlockingQueue;

public class Test06 {
    public static void main(String[] args){
        LinkedBlockingQueue linkedBlockingQueue = new LinkedBlockingQueue(3);
        linkedBlockingQueue.add("张三");
        linkedBlockingQueue.add("李四");
        linkedBlockingQueue.add("王五");
        System.out.println(linkedBlockingQueue.size());
    }
}

8 使用BlockingQueue模拟生产者与消费者代码示例:
package com.lijie;
 
import java.util.concurrent.ArrayBlockingQueue;
 
public class BlockingQueueTest {
 
    //final成员变量表示常量,只能被赋值一次,赋值后值不再改变。
    private static final int queueSize = 5;
    private static final ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<String>(queueSize);
    private static final int produceSpeed = 300;//生产速度
    private static final int consumeSpeed = 500;//消费速度
 
    //生产者
    public static class Producer implements Runnable {

        public void run() {
            while (true) {
                try {
                    System.out.println("老板准备炸油条了,架子上还能放:" + (queueSize - queue.size()) + "根油条");
                    queue.put("1根油条");
                    System.out.println("老板炸好了1根油条,架子上还能放:" + (queueSize - queue.size()) + "根油条");
                    Thread.sleep(produceSpeed);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
 
    //消费者
    public static class Consumer implements Runnable {
 
        public void run() {
            while (true) {
                try {
                    System.out.println("A 准备买油条了,架子上还剩" + queue.size() + "根油条");
                    queue.take();
                    System.out.println("A 买到1根油条,架子上还剩" + queue.size() + "根油条");
                    Thread.sleep(consumeSpeed);
 
                    System.out.println("B 准备买油条了,架子上还剩" + queue.size() + "根油条");
                    queue.take();
                    System.out.println("B 买到1根油条,架子上还剩" + queue.size() + "根油条");
                    Thread.sleep(consumeSpeed);
 
                    System.out.println("C 准备买油条了,架子上还剩" + queue.size() + "根油条");
                    queue.take();
                    System.out.println("C 买到1根油条,架子上还剩" + queue.size() + "根油条");
                    Thread.sleep(consumeSpeed);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
 
 
    public static void main(String[] args) {
        Thread producer = new Thread(new Producer());
        Thread consumer = new Thread(new Consumer());
        producer.start();
        consumer.start();
    }
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 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、付费专栏及课程。

余额充值