常用并发工具类(并发集合类)

文章目录

  • 概述
  • BlockingQueue
  • ArrayBlockingQueue
    • 数据存储相关属性
    • 阻塞特性相关属性
    • 主要方法
  • LinkedBlockingQueue
    • LinkedBlockingQueue 主要属性
    • LinkedBlockingQueue 设计思想
  • ConcurrentLinkedQueue
  • PriorityBlockingQueue
    • PriorityBlockingQueue 主要属性
    • PriorityBlockingQueue 设计思想
  • SynchronousQueue
  • LinkedBlockingDeque
  • DelayQueue
  • DelayQueue 主要属性
    • DelayQueue 主要设计思想
  • CopyOnWrite
  • CopyOnWriteArrayList
    • CopyOnWriteArrayList 的主要属性
    • CopyOnWriteArrayList 主要设计思想
    • CopyOnWriteArraySet

概述

常用的并发集合类,主要有 ArrayBlockingQueueConcurrentLinkedQueueLinkedBlockingQueuePriorityBlockingQueueDelayQueueSynchronousQueueLinkedBlockingDeque,以及 COW 系列的集合,如 CopyOnWriteArrayListCopyOnWriteArraySet

BlockingQueue

BlockingQueue 描述了一个阻塞队列应该有的行为,例如:

  • 阻塞读方法:take()
  • 阻塞写方法:put()

阻塞的读写方法的函数签名上都会抛出 InterruptedException,具体的阻塞逻辑由实现类自行实现。

ArrayBlockingQueue

是一个基于数组实现的阻塞队列,创建对象时必须指定容量

数据存储相关属性

内置了一个循环队列用于存储数据,可以更高效地利用内存空间

    /** The queued items */final Object[] items;/** items index for next take, poll, peek or remove */int takeIndex;/** items index for next put, offer, or add */int putIndex;/** Number of elements in the queue */int count;

阻塞特性相关属性

内置一个 ReentrantLock 锁对象,一个队列是否为空的 Condition 对象,以及一个队列是否满的 Condition 对象

    /** Main lock guarding all access */final ReentrantLock lock;/** Condition for waiting takes */private final Condition notEmpty;/** Condition for waiting puts */private final Condition notFull;

其中,notEmpty 条件和 notFull 条件都是由 lock 对象创建出来的

主要方法

  • 构造方法中必须传入容量,可以通过 public ArrayBlockingQueue(int capacity, boolean fair) 中的 fair 参数来指定公平/非公平模式
    • fair 参数最终会用于构造 ReentrantLock 锁对象
  • put 方法会在队列满的时候((队尾下标+1)%队列长度=队头下标))调用 notEmpty 条件进行等待,等队列再次有空位的时候(有元素出队时将会被唤醒)将元素放至队尾
  • take 会在队列为空的时候(队尾下标=队头下标)调用 notFull 条件进行等待,当队列不为空时,将队头的元素返回,并删除队头元素

LinkedBlockingQueue

LinkedBlockingQueue 是一个基于单向链表实现的阻塞队列,其容量为:

  • 如果在构造函数中指定了容量,那么容量就是队列长度的最大边界限制
  • 如果没有在构造函数中指定容量,容量将会默认设置成 Integer.MAX_VALUE,近似无界

所以,LinkedBlockingQueue 在不传入容量的情况下,最大容量会被设置为 Integer.MAX_VALUE

LinkedBlockingQueue 主要属性

    /** The capacity bound, or Integer.MAX_VALUE if none */private final int capacity;/** Current number of elements */private final AtomicInteger count = new AtomicInteger();/*** Head of linked list.* Invariant: head.item == null*/transient Node<E> head;/*** Tail of linked list.* Invariant: last.next == null*/private transient Node<E> last;/** Lock held by take, poll, etc */private final ReentrantLock takeLock = new ReentrantLock();/** Wait queue for waiting takes */private final Condition notEmpty = takeLock.newCondition();/** Lock held by put, offer, etc */private final ReentrantLock putLock = new ReentrantLock();/** Wait queue for waiting puts */private final Condition notFull = putLock.newCondition();
  • LinkedBlockingQueue 底层的数据结构是单向链表
  • 内置一个读取锁,ReentrantLock takeLock ,以及由这把锁创建出来的非空条件 Condition notEmpty
  • 内置一个写入锁,ReentrantLock putLock,以及由这把锁创建出来的非空条件 Condition notFull

LinkedBlockingQueue 设计思想

  • 执行 take() 方法的时候,会从队列的头部取出元素,所以必须要获取读取锁 takLock,从而确保同时只有一个线程可以操作链表的头部
    • 当执行 take() 方法的时候如果队列为空,那么当前线程将会调用 notEmpty.await() 方法进行阻塞,一直到有元素放入事件发生(如调用 put() 方法)
  • 执行 put() 方法的时候,会从队列的尾部添加元素,所以必须要获取到写入锁 putLock,从而确保同时只有一个线程可以操作链表的尾部
    • 当执行 put() 方法的时候如果队列满了,那么当前线程将会调用 notFull.await() 方法进行阻塞,一直到有元素取出事件发生(如调用 take() 方法)
  • 当执行 remove()contains() 等方法时,将会同时获取读、写两把锁,在方法结束后同时释放两把锁
    • 这样做的目的是因为 remove() 等方法不能确定在链表的某个位置操作(读取或者新增、删除)元素,所以需要同时对队头和队尾都加锁

总的来说,LinkedBlockingQueue 就是通过控制链表同一方向的操作,来实现线程安全和读写条件阻塞;当不能确定操作元素的位置时,将进行双重加锁

ConcurrentLinkedQueue

ConcurrentLinkedQueue 是一个基于单向链表实现的无界线程安全队列,基于 CAS + 自旋来保证线程安全

PriorityBlockingQueue

PriorityBlockingQueue 是一个基于优先级的无界阻塞队列,底层的数据结构是

PriorityBlockingQueue 主要属性

    /*** Priority queue represented as a balanced binary heap: the two* children of queue[n] are queue[2*n+1] and queue[2*(n+1)].  The* priority queue is ordered by comparator, or by the elements'* natural ordering, if comparator is null: For each node n in the* heap and each descendant d of n, n <= d.  The element with the* lowest value is in queue[0], assuming the queue is nonempty.*/private transient Object[] queue;/*** The number of elements in the priority queue.*/private transient int size;/*** The comparator, or null if priority queue uses elements'* natural ordering.*/private transient Comparator<? super E> comparator;/*** Lock used for all public operations*/private final ReentrantLock lock;/*** Condition for blocking when empty*/private final Condition notEmpty;/*** Spinlock for allocation, acquired via CAS.*/private transient volatile int allocationSpinLock;
  • PriorityBlockingQueue 底层有一个可扩容的对象数组 Object[] queue,结构是
  • 内置一个 ReentrantLock 独占锁,以及由这个锁构造出来的 Condition notEmpty 条件
  • 一个 volatile 关键字修饰的整型变量 volatile int allocationSpinLock

PriorityBlockingQueue 设计思想

  • 所有的存取方法都需要获取锁后才能执行,包括 size() 方法
  • 当数组的长度大于等于容量时,需要进行扩容操作
    • 扩容之前先要释放锁
    • 首先要尝试使用 CAS 尝试把 allocationSpinLock 变量的值修改为 1
    • 当前容量小于 64 时,增长为原来的 2 倍 + 2;否则增长为原来的 1.5
    • 还原 allocationSpinLock 的值
    • 扩容完成之后,再次获取锁
  • 插入元素时,会按照指定的 Comparator 的比较方法进行插入;否则将会使用元素自身的 Comparable.compareTo() 方法进行比较
  • 调用 take() 方法时如果队列为空时,当前线程将会调用 notEmpty.await() 进行阻塞,直到有新的元素入队后被唤醒
  • 出队的元素始终是堆顶元素

SynchronousQueue

一个不存储元素的阻塞队列,每一个入队操作必须对应一个出队操作,每一个出队操作必须对应一个入队操作。

LinkedBlockingDeque

LinkedBlockingQueue 是一个双端阻塞队列,底层数据结构是双向链表。

  • 内置一个 ReentrantLock 独占锁,以及由这个锁构造出来的 Condition notEmpty(队列为空,等待放入) 和 Condition notFull(队列已满,等待取出)条件
  • 当队列为空时调用阻塞取出相关方法(take())时,将会调用 notEmpty.await() 方法进行阻塞,直到有元素被放入到队列中
  • 当队列已满时调用阻塞存入相关方法(put())时,将会调用 notFull.await() 方法进行阻塞,直到有元素被取出
  • 可以从队列的两端(头部/尾部)进行元素操作(存入/取出)元素,意味着可以使用 LinkeBlockingQueue 自由实现栈或者队列

DelayQueue

DelayQueue 是一个无界延时阻塞队列。底层的数据结构是优先级队列 PriorityQueue

DelayQueue 主要属性

private final transient ReentrantLock lock = new ReentrantLock();private final PriorityQueue<E> q = new PriorityQueue<E>();private Thread leader = null;/*** Condition signalled when a newer element becomes available* at the head of the queue or a new thread may need to* become leader.*/private final Condition available = lock.newCondition();
  • 内置一个 ReentrantLock 独占锁,以及一个由锁对象创建出来的条件对象 Condition available
  • 底层数据结构是 PriorityQueue,是由堆来实现的
  • 有一个 Thread leader 属性,代表的是当前第一个尝试等待获取队头元素的线程

DelayQueue 主要设计思想

  • 所有放入到 DelayQueue 中的元素,都要实现 Delayed 接口
  • 当队列为空时调用出队方法,将会调用 available.await() 方法进行阻塞,直到有新的元素被添加到队列中
  • 当队列不为空时调用出队方法,首先判断队头元素的剩余过期时间是否小于等于 0
    • 如果队头元素的剩余过期时间小于等于 0,那么说明元素已经到期,直接把这个元素移除并返回
    • 如果队头元素的剩余过期时间(long delay)大于 0,将会判断当前 leader 是否为空
      • 如果为空,则把自己设置为 leader,并且调用 available.awaitNanos(long delay) 方法,将自己阻塞 long delay 时间
      • 如果不为空,说明已经有其他线程在等待获取元素了,那么将调用 available.await() 方法进行无限期等待
    • 在阻塞了 long delay 时间后,重新将 leader 置为 null,并且执行上面的逻辑
  • 所有存取方法调用前都需要获取锁

CopyOnWrite

CopyOnWrite,顾名思义,就是写时复制的意思。
主要的设计思想就是在修改数据的时候,并不直接在原数据上进行修改,而是先拷贝一份原数据的副本,后续对于数据的修改操作在拷贝出来的副本上进行,最后将副本替换掉原数据。
这样做的好处就是,在读取数据的时候,是不用采取任何并发控制手段的,直接访问源数据就行,所以写时复制适用于读多写少的场景。

CopyOnWriteArrayList

CopyOnWriteArrayList 是一个并发安全的动态数组,底层数据结构是数组。

CopyOnWriteArrayList 的主要属性

    /** The lock protecting all mutators */final transient ReentrantLock lock = new ReentrantLock();/** The array, accessed only via getArray/setArray. */private transient volatile Object[] array;
  • 内置一个 ReentrantLock 独占锁
  • 一个用 volatile 关键字修饰的对象数组 array

CopyOnWriteArrayList 主要设计思想

  • 在所有的修改方法执行之前,首先要获取锁
  • 在使用元素添加方法时,每次会拷贝出一个长度 +1 的新数组,同时把要添加的元素放入到新数组的最后一个位置上,最后把旧数组替换成新数组
  • 在使用元素移除方法时,每次会拷贝出一个长度 -1 的新数组,最后根据要删除的元素位置,进行数组拷贝,最后把旧数组替换成新数组
  • 所有读取方法都没有采取任何同步措施,包括获取迭代器
    • 获取迭代器的方法,并没有像很多文章中说的对当前数组进行了一个拷贝,而是直接把当前数组的引用传递给了迭代器
    • 之所以不用对当前数组进行拷贝,是因为每次的修改方法都会创建出一个新数组,所以 CopyOnWriteArrayList 迭代器根本不会,也没有必要去拷贝当前数组

CopyOnWriteArraySet

CopyOnWriteArraySet 是一个并发安全的无重复集合,底层是基于 CopyOnWriteArrayList 来实现的

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/557278.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

参考文献起止页码怎么写_毕业论文文献综述不会写?快来看看这篇文章(附含通用模板)...

文献综述是对所研究主题的现状进行客观的叙述和评论、寻求新的研究突破点。一个资料全面、研究深入的综述不仅可以帮助作者确立毕业论文的选题&#xff0c;还可以为论文的深入研究提供有力的支撑。本文分享一份"毕业论文文献综述万能模板",以供参考。一、文献综述的基…

常用并发工具类(线程池)

文章目录概述ThreadPoolExecutorThreadPoolExecutor 的主要属性Worker 主要属性线程池的状态线程池的状态流转线程池提交任务的执行流程线程数量的设置线程池的种类FixedThreadPoolCachedThreadPoolSingleThreadExecutorScheduledThreadPoolExecutorSingleThreadScheduledExecu…

【Java 8 新特性】Java Stream.of()用法示例

本页将介绍Java Stream.of方法示例。Stream.of用于为给定元素创建顺序流。我们可以传递单个元素或多个元素。 查看javadoc中Stream.of方法声明。 static <T> Stream<T> of(T t) 参数&#xff1a;传递单个元素。 返回&#xff1a;该方法返回一个包含一个元素的流。…

Java 类加载机制

文章目录概述类的生命周期类加载的时机类加载的主要 5 个阶段加载验证准备准备阶段初始值的含义解析符号引用直接引用解析阶段的理解静态绑定与动态绑定初始化类加载器类加载器与类之间的关系类加载器的种类双亲委派机制双亲委派机制设计目的破坏双亲委派机制破坏双亲委派机制的…

Java –什么是瞬态字段?

在Java中&#xff0c; transient字段在序列化过程中被排除。 简而言之&#xff0c;当我们将对象保存到文件中&#xff08;序列化&#xff09;时&#xff0c;所有transient字段都将被忽略。 1. POJO 瞬态 复查以下Person类&#xff1b; 薪水领域是transient 。 public class …

JVM 内存模型与内存分配方式

文章目录JVM 内存模型概述基于分代收集理论设计的垃圾收集器所管理的堆结构方法区的演变内存分配划分内存的方法划分内存时如何解决并发问题对象栈上分配基于分代收集理论的垃圾收集器管理下的内存分配规则对象优先分配在 Eden 区大对象直接进入老年代长期存活的对象将逐步进入…

image pil 图像保存_如何利用python中的PIL库做图像处理?

自从这个世界上出现了Python编程&#xff0c;一切都好像有了新的思路与进展&#xff0c;比如人工智能&#xff0c;还有我们常用的PS&#xff0c;你可知道Python也可以做图像处理&#xff0c;用的就是PIL库&#xff0c;还没有用过的&#xff0c;还没有发现的&#xff0c;还没有实…

GSON详解

GSON GSON弥补了JSON的许多不足的地方&#xff0c;在实际应用中更加适用于Java开发。在这里&#xff0c;我们主要讲解的是利用GSON来操作java对象和json数据之间的相互转换&#xff0c;包括了常见的对象序列化和反序列化的知识。 一、前言 因为json有2种类型&#xff1a; 一…

机器人 瓷砖墙面清洗_墙壁清洁机器人解析

1第一章绪论1.1课题的背景、目的及意义[1]壁面清洗爬壁机器人属于移动式服务机器人的一种&#xff0c;可在垂直壁面或顶部移动&#xff0c;完成其外表面的清洗作业。在工业机器人问世30多年后的今天&#xff0c;它已被世人看作是一种生产工具&#xff0c;在制造、装配及最近在服…

内存回收算法与 Hot Spot 算法实现细节

文章目录内存回收算法概述对象存活判定算法引用计数算法可达性分析算法垃圾收集算法分代收集理论标记-清除算法标记-复制算法半区复制算法Appel 式复制算法Appel 式复制算法的逃生门设计标记-整理算法HotSpot 虚拟机实现细节GC Root 枚举Hot Spot 实现 GC Root 枚举安全点与安全…

link st 量产工具_ST-Link资料03_ST-Link固件升级、驱动下载安装方法

说明&#xff1a;本文原创作者『strongerHuang』本文首发于微信公众号『嵌入式专栏』&#xff0c;同时也更新在我的个人网站&#xff1a;EmbeddedDevelop一、写在前面前两篇文章讲述的都是关于ST-Link的一些理论知识&#xff0c;建议初学者看看&#xff1a;ST-Link资料01_ST-Li…

Java 主流垃圾收集器

文章目录垃圾收集器概述Serial 与 Serial Old 垃圾收集器Serial 与 Serial Old 垃圾收集器总结ParNew 垃圾收集器Parallel Scavenge 垃圾收集器Parallel Scavenge 的吞吐量控制参数Parallel Scavenge 的自适应调节策略Parallel Scavenge 垃圾收集器总结ParNew 和 Parallel Scav…

SSM 框架整合 spring 发送邮件功能实现!

基于SSM的 spring 发送邮件的实现 由于考虑到项目中需要&#xff0c;如果程序出现异常或其它问题&#xff0c;可以发送邮件来及时提醒后台维护人员处>理。所以目前考虑使用JavaMail来实现邮件发送&#xff01;如下&#xff0c;是我整理的一些内容&#xff0c;做个笔记记录下…

Java 故障处理与性能监控工具

文章目录概述基础工具jpsjstatjinfojmapjhatjstack高级工具VisualVMVisualVM 的主要功能ArthasGC Easy概述 在使用 Java 语言进行开发的过程中&#xff0c;我们很可能会遇到各种程序问题。 比如&#xff0c;可能会遇见程序突然就静止不动了&#xff0c;但是程序进程依然显示在…

SSM整合框架实现发送邮件功能

SSM整合框架实现发送邮件功能 1.导入发送邮件的依赖 <!-- 发送邮件jar包--><!--spring支持--><dependency><groupId>org.springframework</groupId><artifactId>spring-context-support</artifactId><version>5.0.0.RELE…

ap接口 php_2018年小米高级 PHP 工程师面试题(模拟考试卷)

点击蓝字关注我们&#xff01;每天获取最新的编程小知识&#xff01;源 / php中文网 源 / www.php.cn在面试之前多看看有关公司的面试资料&#xff0c;对之后的面试会很有帮助。今天就给大家带来2018年小米高级 PHP 工程师面试题(模拟考试卷)&#xff0c;有着一定的参考价…

composer 设置版本号_Composer依赖管理 – PHP的利器

别再到处搜PHP类扩展包了&#xff0c;对于现代语言而言&#xff0c;包管理器基本上是标配。Java 有 Maven&#xff0c;Python 有 pip&#xff0c;Ruby 有 gem&#xff0c;Nodejs 有 npm。PHP 的则是 PEAR&#xff0c;不过 PEAR 坑不少&#xff1a;依赖处理容易出问题配置非常复…

LeetCode.31 下一个排列

题目描述 实现获取下一个排列的算法&#xff0c;算法需要将给定的数组&#xff0c;重新排列成下一个更大的排列&#xff08;即组合出下一个更大的数字&#xff09;。 如果不存在下一个更大的排列&#xff0c;则将数字重新排列成最小的排列。 题目思路 我们可以使用如下的思路…

SpringBoot2.5.4发送邮件4种方式

一.准备 在创建SpringBoot项目 二、选择依赖 选择依赖时 发现其选择依赖时有邮件发送与Springboot整合的jar包&#xff0c;我们勾选即可 如果未勾选也不要紧&#xff0c;咱们手动导入 <dependency><groupId>org.projectlombok</groupId><artifactId…

手游传奇刷元宝_传奇手游 平民制霸刀刀爆元宝!

新轩辕神途手游游戏介绍新轩辕神途是一款玩法种类十分丰富多样的神途手游&#xff0c;游戏内拥有放置挂机升级玩法&#xff0c;玩家不用浪费时间工作上学都能自动升级打宝&#xff0c;更有十分庞大的世界地图等你来探索&#xff0c;十分靠谱&#xff0c;更受欢迎&#xff0c;爆…