java中并发Queue种类与各自API特点

简介: java中并发Queue种类与各自API特点

一 先说下队列
队列是一种数据结构.它有两个基本操作:在队列尾部加入一个元素,和从队列头部移除一个元素(注意不要弄混队列的头部和尾部)

就是说,队列以一种先进先出的方式管理数据,如果你试图向一个 已经满了的阻塞队列中添加一个元素或者是从一个空的阻塞队列中移除一个元索,将导致线程阻塞.
在多线程进行合作时,阻塞队列是很有用的工具。工作者线程可以定期地把中间结果存到阻塞队列中而其他工作者线程把中间结果取出并在将来修改它们。队列会自动平衡负载。
如果第一个线程集运行得比第二个慢,则第二个 线程集在等待结果时就会阻塞。如果第一个线程集运行得快,那么它将等待第二个线程集赶上来.
说白了,就是先进先出,线程安全!
java中并发队列都是在java.util.concurrent并发包下的,Queue接口与List、Set同一级别,都是继承了Collection接口,最近学习了java中的并发Queue的所有子类应用场景,这里记录分享一下:
1.1 这里可以先用wait与notify(脑忒fai) 模拟一下队列的增删数据,简单了解一下队列:
import java.util.LinkedList;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**

  • 模拟队列增删数据
  • @author houzheng
    */
    public class MyQueue {
    //元素集合
    private LinkedList list=new LinkedList();
    //计数器(同步),判断集合元素数量
    private AtomicInteger count=new AtomicInteger();
    //集合上限与下限,final必须指定初值
    private final int minSize=0;
    private final int maxSize;
    //构造器指定最大值
    public MyQueue(int maxSize) {
     this.maxSize = maxSize;
    

    }

    //初始化对象,用于加锁,也可直接用this
    private Object lock=new Object();
    //put方法:往集合中添加元素,如果集合元素已满,则此线程阻塞,直到有空间再继续
    public void put(Object obj){

     synchronized (lock) {
         while(count.get()==this.maxSize){
             try {
                 lock.wait();
             } catch (InterruptedException e) {
                 e.printStackTrace();}
         }
         list.add(obj);
         //计数器加一
         count.incrementAndGet();
         System.out.println("放入元素:"+obj);
         //唤醒另一个线程,(处理极端情况:集合一开始就是空,此时take线程会一直等待)
         lock.notify();
     }
    

    //take方法:从元素中取数据,如果集合为空,则线程阻塞,直到集合不为空再继续
    public Object take(){

     Object result=null;
     synchronized(lock){
         while(count.get()==this.minSize){
         //移除第一个
         result=list.removeFirst();
         //计数器减一
         count.decrementAndGet();
         System.out.println("拿走元素:"+result);
         //唤醒另一个线程,(处理极端情况:集合一开始就是满的,此时put线程会一直等待)
     return result;
    

    public int getSize(){

     return this.count.get();
    

    //代码效果参考:http://www.zidongmutanji.com/zsjx/104891.html
    public static void main(String[] args) {
    //创建集合容器
    MyQueue queue=new MyQueue(5);
    queue.put("1");
    queue.put("2");
    queue.put("3");
    queue.put("4");
    queue.put("5");
    System.out.println("当前容器长度为:"+queue.getSize());
    Thread t1=new Thread(()->{
    queue.put("6");
    queue.put("7");
    },"t1");
    Thread t2=new Thread(()->{
    Object take1 = queue.take();
    Object take2 = queue.take();
    },"t2");
    //测试极端情况,两秒钟后再执行另一个线程
    t1.start();
    try {
    TimeUnit.SECONDS.sleep(2);
    } catch (InterruptedException e) {
    e.printStackTrace();
    t2.start();
    }
      这里用线程通信的方式简单模拟了队列的进出,那么接下来就正式进入java的并发队列:
    二 并发Queue
    JDK中并发队列提供了两种实现,一种是高性能队列ConcurrentLinkedQueue,一种是阻塞队列BlockingQueue,两种都继承自Queue:
    回到顶部
    1 ConcurrentLinkedQueue
    这是一个使用于高并发场景的队列(额,各位看这块博客的小朋友,最好对线程基础比较熟悉再来看,当然我也在拼命学习啦,哈哈哈),主要是无锁的方式,他的想能要比BlockingQueue好
    ,是基于链接节点的无界线程安全队列,先进先出,不允许有null元素,废话不多说,上demo:
    这种queue比较简单,没什么好说的,和ArrayList一样用就可以,关键是BlockingQUeue
    2 BlockingQueue
    blockingQueue主要有5中实现,我感觉都挺有意思的,其中几种还比较常用就都学习了下,这里都介绍下:
    2.1 ArrayBlockingQueue
    @Test
    public void test02() throws Exception{
    //必须指定队列长度
    ArrayBlockingQueue abq=new ArrayBlockingQueue(2);
    abq.add("a");
    //add :添加元素,如果BlockingQueue可以容纳,则返回true,否则抛异常,支持添加集合
    System.out.println(abq.offer("b"));//容量如果不够,返回false
    //offer: 如果可能的话,添加元素,即如果BlockingQueue可以容纳,则返回true,否则返回false,支持设置超时时间
    //设置超时,如果超过时间就不添加,返回false,
    abq.offer("d", 2, TimeUnit.SECONDS);// 添加的元素,时长,单位
    //put 添加元素,如果BlockQueue没有空间,则调用此方法的线程被阻断直到BlockingQueue里面有空间再继续.
    abq.put("d");//会一直等待
    //poll 取走头部元素,若不能立即取出,则可以等time参数规定的时间,取不到时返回null,支持设置超时时间
    abq.poll();
    abq.poll(2,TimeUnit.SECONDS);//两秒取不到返回null
    //take() 取走头部元素,若BlockingQueue为空,阻断进入等待状态直到Blocking有新的对象被加入为止
    abq.take();
    //取出头部元素,但不删除
    abq.element();
    //drainTo()
    //一次性从BlockingQueue获取所有可用的数据对象(还可以指定获取数据的个数),通过该方法,可以提升获取数据效率;不需要多次分批加锁或释放锁。
    List list=new ArrayList();
    abq.drainTo(list,2);//将队列中两个元素取到list中,取走后队列中就没有取走的元素
    System.out.println(list); //[a,b]
    System.out.println(abq); //[]
    }  

    //代码效果参考:http://www.zidongmutanji.com/bxxx/517398.html
    2.2 LinkedBlockingQueue
    public void test03(){
    LinkedBlockingQueue lbq=new LinkedBlockingQueue();//可指定容量,也可不指定
    lbq.add("a");
    lbq.add("b");
    lbq.add("c");
    //API与ArrayBlockingQueue相同
    //是否包含
    System.out.println(lbq.contains("a"));
    //移除头部元素或者指定元素 remove("a")
    System.out.println(lbq.remove());
    //转数组
    Object[] array = lbq.toArray();
    //element 取出头部元素,但不删除
    System.out.println(lbq.element());
      
    2.3 SynchronousQueue
    public static void main(String[] args) {
    SynchronousQueue sq=new SynchronousQueue();
    // iterator() 永远返回空,因为里面没东西。
    // peek() 永远返回null
    /**

     * isEmpty()永远是true。
     * remainingCapacity() 永远是0。
     * remove()和removeAll() 永远是false。
     */
    new Thread(()->{
            //取出并且remove掉queue里的element(认为是在queue里的。。。),取不到东西他会一直等。
            System.out.println(sq.take());
    
    }).start();
            //offer() 往queue里放一个element后立即返回,
            //如果碰巧这个element被另一个thread取走了,offer方法返回true,认为offer成功;否则返回false
            //true ,上面take线程一直在等,
            ////下面刚offer进去就被拿走了,返回true,如果offer线程先执行,则返回false
            System.out.println(sq.offer("b"));
    
        } catch (Exception e) {
            //往queue放进去一个element以后就一直wait直到有其他thread进来把这个element取走
            sq.put("a");
    

    //代码效果参考:http://www.zidongmutanji.com/bxxx/43852.html
    2.4 PriorityBlockingQueue
    public void test04() throws Exception{
    //队列里元素必须实现Comparable接口,用来决定优先级
    PriorityBlockingQueue pbq=new PriorityBlockingQueue();
    pbq.add("b");
    pbq.add("g");
    pbq.add("a");
    pbq.add("c");
    //获取的时候会根据优先级取元素,插入的时候不会排序,节省性能
    //System.out.println(pbq.take());//a,获取时会排序,按优先级获取
    System.out.println(pbq.toString());//如果前面没有取值,直接syso也不会排序
    Iterator iterator = pbq.iterator();
    while(iterator.hasNext()){
    System.out.println(iterator.next());
    public void test05(){
    PriorityBlockingQueue pbq=new PriorityBlockingQueue();
    Person p2=new Person("姚振",20);
    Person p1=new Person("侯征",24);
    Person p3=new Person("何毅",18);
    Person p4=new Person("李世彪",22);
    pbq.add(p1);
    pbq.add(p2);
    pbq.add(p3);
    pbq.add(p4);
    System.out.println(pbq);//没有按优先级排序
    try {
    //只要take获取元素就会按照优先级排序,获取一次就全部排好序了,后面就会按优先级迭代
    pbq.take();
    } catch (InterruptedException e) {
    e.printStackTrace();
    //按年龄排好了序
    for (Iterator iterator = pbq.iterator(); iterator.hasNext();) {
    Person person = (Person) iterator.next();
    System.out.println(person);
    2.5 最后说一下DelayQueue ,这里用个网上很经典的例子,网吧上网计时
    网民实体queue中元素
    //网民
    public class Netizen implements Delayed {
    //身份证
    private String ID;
    //名字
    private String name;
    //上网截止时间
    private long playTime;
    //比较优先级,时间最短的优先
    @Override
    public int compareTo(Delayed o) {
    Netizen netizen=(Netizen) o;
    return this.getDelay(TimeUnit.SECONDS)-o.getDelay(TimeUnit.SECONDS)>0?1:0;
    public Netizen(String iD, String name, long playTime) {
    ID = iD;
    this.name = name;
    this.playTime = playTime;
    //获取上网时长,即延时时长
    public long getDelay(TimeUnit unit) {
    //上网截止时间减去现在当前时间=时长
    return this.playTime-System.currentTimeMillis();
     网吧类:
    //网吧
    public class InternetBar implements Runnable {
    //网民队列,使用延时队列
    private DelayQueue dq=new DelayQueue();
    //上网
    public void startPlay(String id,String name,Integer money){
    //截止时间= 钱数时间+当前时间(1块钱1秒)
    Netizen netizen=new Netizen(id,name,1000
    money+System.currentTimeMillis());
    System.out.println(name+"开始上网计费......");
    dq.add(netizen);
    //时间到下机
    public void endTime(Netizen netizen){
    System.out.println(netizen.getName()+"余额用完,下机");
    public void run() {
    //线程,监控每个网民上网时长
    while(true){
    //除非时间到.否则会一直等待,直到取出这个元素为止
    Netizen netizen=dq.take();
    endTime(netizen);
    catch (InterruptedException e) {

    //新建一个网吧
    InternetBar internetBar=new InternetBar();
    //来了三个网民上网
    internetBar.startPlay("001","侯征",3);
    internetBar.startPlay("002","姚振",7);
    internetBar.startPlay("003","何毅",5);
        Thread t1=new Thread(internetBar);
    

相关文章
|
14天前
|
算法 Java 程序员
菜鸟之路Day06一一Java常用API
《菜鸟之路Day06——Java常用API》由blue编写,发布于2025年1月24日。本文详细介绍了Java中常用的API,包括JDK7的时间类(Date、SimpleDateFormat、Calendar)和JDK8新增的时间API(ZoneId、Instant、DateTimeFormatter等),以及包装类的使用。通过多个实例练习,如时间计算、字符串转整数、十进制转二进制等,帮助读者巩固所学内容,提升编程技能。文章强调了理论与实践结合的重要性,鼓励读者多做练习以提高学习效率。
73 28
|
30天前
|
JSON Java 数据挖掘
利用 Java 代码获取淘宝关键字 API 接口
在数字化商业时代,精准把握市场动态与消费者需求是企业成功的关键。淘宝作为中国最大的电商平台之一,其海量数据中蕴含丰富的商业洞察。本文介绍如何通过Java代码高效、合规地获取淘宝关键字API接口数据,帮助商家优化产品布局、制定营销策略。主要内容包括: 1. **淘宝关键字API的价值**:洞察用户需求、优化产品标题与详情、制定营销策略。 2. **获取API接口的步骤**:注册账号、申请权限、搭建Java开发环境、编写调用代码、解析响应数据。 3. **注意事项**:遵守法律法规与平台规则,处理API调用限制。 通过这些步骤,商家可以在激烈的市场竞争中脱颖而出。
|
2月前
|
JSON Java Apache
Java基础-常用API-Object类
继承是面向对象编程的重要特性,允许从已有类派生新类。Java采用单继承机制,默认所有类继承自Object类。Object类提供了多个常用方法,如`clone()`用于复制对象,`equals()`判断对象是否相等,`hashCode()`计算哈希码,`toString()`返回对象的字符串表示,`wait()`、`notify()`和`notifyAll()`用于线程同步,`finalize()`在对象被垃圾回收时调用。掌握这些方法有助于更好地理解和使用Java中的对象行为。
|
2月前
|
算法 Java API
如何使用Java开发获得淘宝商品描述API接口?
本文详细介绍如何使用Java开发调用淘宝商品描述API接口,涵盖从注册淘宝开放平台账号、阅读平台规则、创建应用并申请接口权限,到安装开发工具、配置开发环境、获取访问令牌,以及具体的Java代码实现和注意事项。通过遵循这些步骤,开发者可以高效地获取商品详情、描述及图片等信息,为项目和业务增添价值。
101 10
|
2月前
|
存储 Java 数据挖掘
Java 8 新特性之 Stream API:函数式编程风格的数据处理范式
Java 8 引入的 Stream API 提供了一种新的数据处理方式,支持函数式编程风格,能够高效、简洁地处理集合数据,实现过滤、映射、聚合等操作。
91 6
|
2月前
|
Java API 开发者
Java中的Lambda表达式与Stream API的协同作用
在本文中,我们将探讨Java 8引入的Lambda表达式和Stream API如何改变我们处理集合和数组的方式。Lambda表达式提供了一种简洁的方法来表达代码块,而Stream API则允许我们对数据流进行高级操作,如过滤、映射和归约。通过结合使用这两种技术,我们可以以声明式的方式编写更简洁、更易于理解和维护的代码。本文将介绍Lambda表达式和Stream API的基本概念,并通过示例展示它们在实际项目中的应用。
|
3月前
|
存储 安全 Java
Java多线程编程中的并发容器:深入解析与实战应用####
在本文中,我们将探讨Java多线程编程中的一个核心话题——并发容器。不同于传统单一线程环境下的数据结构,并发容器专为多线程场景设计,确保数据访问的线程安全性和高效性。我们将从基础概念出发,逐步深入到`java.util.concurrent`包下的核心并发容器实现,如`ConcurrentHashMap`、`CopyOnWriteArrayList`以及`BlockingQueue`等,通过实例代码演示其使用方法,并分析它们背后的设计原理与适用场景。无论你是Java并发编程的初学者还是希望深化理解的开发者,本文都将为你提供有价值的见解与实践指导。 --- ####
|
3月前
|
安全 Java API
Java中的Lambda表达式与Stream API的高效结合####
探索Java编程中Lambda表达式与Stream API如何携手并进,提升数据处理效率,实现代码简洁性与功能性的双重飞跃。 ####
42 0
|
6月前
|
安全 Java 调度
解锁Java并发编程高阶技能:深入剖析无锁CAS机制、揭秘魔法类Unsafe、精通原子包Atomic,打造高效并发应用
【8月更文挑战第4天】在Java并发编程中,无锁编程以高性能和低延迟应对高并发挑战。核心在于无锁CAS(Compare-And-Swap)机制,它基于硬件支持,确保原子性更新;Unsafe类提供底层内存操作,实现CAS;原子包java.util.concurrent.atomic封装了CAS操作,简化并发编程。通过`AtomicInteger`示例,展现了线程安全的自增操作,突显了这些技术在构建高效并发程序中的关键作用。
89 1
|
3月前
|
存储 设计模式 分布式计算
Java中的多线程编程:并发与并行的深度解析####
在当今软件开发领域,多线程编程已成为提升应用性能、响应速度及资源利用率的关键手段之一。本文将深入探讨Java平台上的多线程机制,从基础概念到高级应用,全面解析并发与并行编程的核心理念、实现方式及其在实际项目中的应用策略。不同于常规摘要的简洁概述,本文旨在通过详尽的技术剖析,为读者构建一个系统化的多线程知识框架,辅以生动实例,让抽象概念具体化,复杂问题简单化。 ####