并发容器11

一 JDK 提供的并发容器总结

JDK 提供的这些容器大部分在 java.util.concurrent 包中。

  • ConcurrentHashMap: 线程安全的 HashMap

  • CopyOnWriteArrayList: 线程安全的 List,在读多写少的场合性能非常好,远远好于 Vector.

  • ConcurrentLinkedQueue: 高效的并发队列,使用链表实现。可以看做一个线程安全的 LinkedList,这是一个非阻塞队列。

  • BlockingQueue: 这是一个接口,JDK 内部通过链表、数组等方式实现了这个接口。表示阻塞队列,非常适合用于作为数据共享的通道。

  • ConcurrentSkipListMap: 跳表的实现。这是一个 Map,使用跳表的数据结构进行快速查找。

二 ConcurrentHashMap

这里救不多赘述了,因为他比较常见

三 CopyOnWriteArrayList

3.1 CopyOnWriteArrayList 简介

public class CopyOnWriteArrayList<E>
extends Object
implements List<E>, RandomAccess, Cloneable, Serializable

在很多应用场景中,读操作可能会远远大于写操作。由于读操作根本不会修改原有的数据,因此对于每次读取都进行加锁其实是一种资源浪费。我们应该允许多个线程同时访问 List 的内部数据,毕竟读取操作是安全的。

这和ReentrantReadWriteLock 读写锁的思想非常类似,也就是读读共享、写写互斥、读写互斥、写读互斥。JDK 中提供了 CopyOnWriteArrayList 类比相比于在读写锁的思想又更进一步。为了将读取的性能发挥到极致,CopyOnWriteArrayList 读取是完全不用加锁的,并且更厉害的是:写入也不会阻塞读取操作。只有写入和写入之间需要进行同步等待。这样一来,读操作的性能就会大幅度提升。那它是怎么做的呢?

3.2 CopyOnWriteArrayList 是如何做到的

CopyOnWriteArrayList 类的所有可变操作(add,set 等等)都是通过创建底层数组的新副本来实现的。当 List 需要被修改的时候,我并不修改原有内容,而是对原有数据进行一次复制,将修改的内容写入副本。写完之后,再将修改完的副本替换原来的数据,这样就可以保证写操作不会影响读操作了。

CopyOnWriteArrayList 的名字就能看出CopyOnWriteArrayList 是满足CopyOnWrite 的 ArrayList,所谓CopyOnWrite 也就是说:在计算机,如果你想要对一块内存进行修改时,我们不在原有内存块中进行写操作,而是将内存拷贝一份,在新的内存中进行写操作,写完之后呢,就将指向原来内存指针指向新的内存,原来的内存就可以被回收掉了。

3.3 CopyOnWriteArrayList 读取和写入源码简单分析

3.3.1 CopyOnWriteArrayList 读取操作的实现

读取操作没有任何同步控制和锁操作,理由就是内部数组 array 不会发生修改,只会被另外一个 array 替换,因此可以保证数据安全。

    /** The array, accessed only via getArray/setArray. */private transient volatile Object[] array;public E get(int index) {return get(getArray(), index);}@SuppressWarnings("unchecked")private E get(Object[] a, int index) {return (E) a[index];}final Object[] getArray() {return array;}

3.3.2 CopyOnWriteArrayList 写入操作的实现

CopyOnWriteArrayList 写入操作 add() 方法在添加集合的时候加了锁,保证了同步,避免了多线程写的时候会 copy 出多个副本出来。

    /*** Appends the specified element to the end of this list.** @param e element to be appended to this list* @return {@code true} (as specified by {@link Collection#add})*/public boolean add(E e) {final ReentrantLock lock = this.lock;lock.lock();//加锁try {Object[] elements = getArray();int len = elements.length;Object[] newElements = Arrays.copyOf(elements, len + 1);//拷贝新数组newElements[len] = e;setArray(newElements);return true;} finally {lock.unlock();//释放锁}}

四 ConcurrentLinkedQueue

Java 提供的线程安全的 Queue 可以分为阻塞队列非阻塞队列,其中阻塞队列的典型例子是 BlockingQueue,非阻塞队列的典型例子是 ConcurrentLinkedQueue,在实际应用中要根据实际需要选用阻塞队列或者非阻塞队列。 阻塞队列可以通过加锁来实现,非阻塞队列可以通过 CAS 操作实现。

从名字可以看出,ConcurrentLinkedQueue这个队列使用链表作为其数据结构.ConcurrentLinkedQueue 应该算是在高并发环境中性能最好的队列了。它之所有能有很好的性能,是因为其内部复杂的实现。

ConcurrentLinkedQueue 内部代码就不分析了,大家知道 ConcurrentLinkedQueue 主要使用 CAS 非阻塞算法来实现线程安全就好了。

ConcurrentLinkedQueue 适合在对性能要求相对较高,同时对队列的读写存在多个线程同时进行的场景,即如果对队列加锁的成本较高则适合使用无锁的 ConcurrentLinkedQueue 来替代

五 BlockingQueue

上面我们己经提到了 ConcurrentLinkedQueue 作为高性能的非阻塞队列。下面我们要讲到的是阻塞队列——BlockingQueue。阻塞队列(BlockingQueue)被广泛使用在“生产者-消费者”问题中,其原因是 BlockingQueue 提供了可阻塞的插入和移除的方法。当队列容器已满,生产者线程会被阻塞,直到队列未满;当队列容器为空时,消费者线程会被阻塞,直至队列非空时为止。

BlockingQueue 是一个接口,继承自 Queue,所以其实现类也可以作为 Queue 的实现来使用,而 Queue 又继承自 Collection 接口。下面是 BlockingQueue 的相关实现类:

下面主要介绍一下:ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue,这三个 BlockingQueue 的实现类。

5.2 ArrayBlockingQueue

5.1 BlockingQueue 简单介绍

ArrayBlockingQueue 是 BlockingQueue 接口的有界队列实现类,底层采用数组来实现。ArrayBlockingQueue 一旦创建,容量不能改变。其并发控制采用可重入锁来控制,不管是插入操作还是读取操作,都需要获取到锁才能进行操作。当队列容量满时,尝试将元素放入队列将导致操作阻塞;尝试从一个空队列中取一个元素也会同样阻塞。

ArrayBlockingQueue 默认情况下不能保证线程访问队列的公平性,所谓公平性是指严格按照线程等待的绝对时间顺序,即最先等待的线程能够最先访问到 ArrayBlockingQueue。而非公平性则是指访问 ArrayBlockingQueue 的顺序不是遵守严格的时间顺序,有可能存在,当 ArrayBlockingQueue 可以被访问时,长时间阻塞的线程依然无法访问到 ArrayBlockingQueue。如果保证公平性,通常会降低吞吐量。如果需要获得公平性的 ArrayBlockingQueue,可采用如下代码:

private static ArrayBlockingQueue<Integer> blockingQueue = new ArrayBlockingQueue<Integer>(10,true);

5.3 LinkedBlockingQueue

LinkedBlockingQueue 底层基于单向链表实现的阻塞队列,可以当做无界队列也可以当做有界队列来使用,同样满足 FIFO 的特性,与 ArrayBlockingQueue 相比起来具有更高的吞吐量,为了防止 LinkedBlockingQueue 容量迅速增,损耗大量内存。通常在创建 LinkedBlockingQueue 对象时,会指定其大小,如果未指定,容量等于 Integer.MAX_VALUE

    /***某种意义上的无界队列* Creates a {@code LinkedBlockingQueue} with a capacity of* {@link Integer#MAX_VALUE}.*/public LinkedBlockingQueue() {this(Integer.MAX_VALUE);}/***有界队列* Creates a {@code LinkedBlockingQueue} with the given (fixed) capacity.** @param capacity the capacity of this queue* @throws IllegalArgumentException if {@code capacity} is not greater*         than zero*/public LinkedBlockingQueue(int capacity) {if (capacity <= 0) throw new IllegalArgumentException();this.capacity = capacity;last = head = new Node<E>(null);}

5.4 PriorityBlockingQueue

PriorityBlockingQueue 是一个支持优先级的无界阻塞队列。默认情况下元素采用自然顺序进行排序,也可以通过自定义类实现 compareTo() 方法来指定元素排序规则,或者初始化时通过构造器参数 Comparator 来指定排序规则。

PriorityBlockingQueue 并发控制采用的是 ReentrantLock,队列为无界队列(ArrayBlockingQueue 是有界队列,LinkedBlockingQueue 也可以通过在构造函数中传入 capacity 指定队列最大的容量,但是 PriorityBlockingQueue 只能指定初始的队列大小,后面插入元素的时候,如果空间不够的话会自动扩容)。

简单地说,它就是 PriorityQueue 的线程安全版本。不可以插入 null 值,同时,插入队列的对象必须是可比较大小的(comparable),否则报 ClassCastException 异常。它的插入操作 put 方法不会 block,因为它是无界队列(take 方法在队列为空的时候会阻塞)。

六 ConcurrentSkipListMap

使用跳表实现 Map 和使用哈希算法实现 Map 的另外一个不同之处是:哈希并不会保存元素的顺序,而跳表内所有的元素都是排序的。因此在对跳表进行遍历时,你会得到一个有序的结果。所以,如果你的应用需要有序性,那么跳表就是你不二的选择。JDK 中实现这一数据结构的类是 ConcurrentSkipListMap。

redis 的章节我已经说过调表,这里就不多赘述了

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

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

相关文章

设计模式入门笔记

1 设计模式简介 在IT这个行业&#xff0c;技术日新月异&#xff0c;可能你今年刚弄懂一个编程框架&#xff0c;明年它就不流行了。 然而即使在易变的IT世界也有很多几乎不变的知识&#xff0c;他们晦涩而重要&#xff0c;默默的将程序员划分为卓越与平庸两类。比如说&#xff…

unity 之参数类型之引用类型

文章目录 引用类型引用类型与值类型的差异 引用类型 在Unity中&#xff0c;引用类型是指那些在内存中存储对象引用的数据类型。以下是在Unity中常见的引用类型的介绍&#xff1a; 节点&#xff08;GameObject&#xff09;&#xff1a; 在Unity中&#xff0c;游戏对象&#xff…

无重叠区间【贪心算法】

无重叠区间 给定一个区间的集合 intervals &#xff0c;其中 intervals[i] [starti, endi] 。返回 需要移除区间的最小数量&#xff0c;使剩余区间互不重叠 。 class Solution {public int eraseOverlapIntervals(int[][] intervals) {//先排序&#xff0c;按照左边界升序,注…

【爬虫】实验项目一:文本反爬网站的分析和爬取

目录 一、实验目的 二、实验预习提示 ​编辑 三、实验内容 四、实验要求 五、实验过程 1. 基本要求&#xff1a; 2. 改进要求A 3. 改进要求B: 六、资料 1.实验框架代码&#xff1a; 2.OpenSSL&#xff1a;Win32/Win64 OpenSSL Installer for Windows - Shining Light…

Node与Express后端架构:高性能的Web应用服务

在现代Web应用开发中&#xff0c;后端架构的性能和可扩展性至关重要。Node.js作为一个基于事件驱动、非阻塞I/O的平台&#xff0c;以及Express作为一个流行的Node.js框架&#xff0c;共同构建了高性能的Web应用服务。 在本文中&#xff0c;我们将深入探讨Node与Express后端架构…

污水厂数字孪生 | 3D可视化管理系统助力污水企业数字化管理

随着城市化进程的不断加快&#xff0c;污水处理成为了城市环境保护的重要组成部分。传统的污水处理方式往往存在诸多问题&#xff0c;如信息不对称、安全隐患等。为了解决这些问题&#xff0c;污水处理3D可视化管控平台应运而生&#xff0c;它通过结合数字孪生技术和远程指导技…

【行为树】py_trees 学习笔记

学习资料 《Introduction to behavior trees》 1. 概念说明 1.1 Action&#xff1a;pt.behaviour.Behaviour 在一个行为树中&#xff0c;Action 是一个叶节点&#xff08;Leaf Node&#xff09;&#xff0c;它实际执行某种行为或任务&#xff0c;并返回成功、失败或运行中的…

Unity关键概念

Unity是一款跨平台的游戏引擎和开发工具&#xff0c;用于创建2D和3D游戏、交互式内容和应用程序。它提供了一个强大的开发环境&#xff0c;使开发者能够轻松地设计、开发和部署高质量的游戏和应用程序。 以下是Unity的几个关键概念&#xff1a; 游戏对象&#xff08;Game Obj…

SpringBoot整合Freemaker结合Vue实现页面填写一键自动生成Redis的配置文件

&#x1f9d1;‍&#x1f4bb;作者名称&#xff1a;DaenCode &#x1f3a4;作者简介&#xff1a;啥技术都喜欢捣鼓捣鼓&#xff0c;喜欢分享技术、经验、生活。 &#x1f60e;人生感悟&#xff1a;尝尽人生百味&#xff0c;方知世间冷暖。 &#x1f4d6;所属专栏&#xff1a;Sp…

管理类联考——逻辑——形式逻辑——汇总篇——知识点突破——假言——各种假言

角度 多重假言 &#xff08;1&#xff09;如果A&#xff0c;那么B&#xff0c;除非C。 符号化为&#xff1a;┐C→ (A→B)。 等价于&#xff1a;┐C→ (┐A∨B)。 等价于&#xff1a;C∨(┐A∨B)。 等价于&#xff1a;C∨┐A∨B。 等价于&#xff1a;┐(C∨┐A&#xff09;→…

035 - datetime和timedstamp

该DATETIME类型用于包含日期和时间部分的值。MySQL检索并DATETIME以格式显示 值 。支持的范围是 到。 YYYY-MM-DD hh:mm:ss1000-01-01 00:00:009999-12-31 23:59:59 该TIMESTAMP数据类型被用于同时包含日期和时间部分的值。 UTCTIMESTAMP的范围是UTC。 1970-01-01 00:00:01203…

素数之谜揭秘:一文详解试除法判断素数

这是我非常喜欢的一道编程题目。不要小看这道题&#xff0c;它看似简单&#xff0c;实则奥妙无穷。由于这是C语言的入门篇&#xff0c;只介绍最简单&#xff0c;也最容易想到的方法&#xff1a;试除法。但哪怕是试除法&#xff0c;也有不少变化。 要想了解试除法&#xff0c;首…

软件测试人需要掌握的测试知识架构体系(上)

软件计划与可行性研究&#xff08;问题定义、可行性研究&#xff09;&#xff1b;需求分析&#xff1b;软件设计&#xff08;概要设计、详细设计&#xff09;&#xff1b;编码&#xff1b;软件测试&#xff1b;运行与维护。 一、软件的生命周期(SDLC) 1、生存周期划分 各阶段…

软件测试/测试开发丨Selenium 高级定位 Xpath

点此获取更多相关资料 本文为霍格沃兹测试开发学社学员学习笔记分享 原文链接&#xff1a;https://ceshiren.com/t/topic/27036 一、xpath 基本概念 XPATH是一门在XML文档中查找信息的语言 XPATH使用路径表达式在XML文档中进行导航 XPATH的应用非常广泛&#xff0c;可以用于UI自…

RT-Thread 线程管理(一)

线程管理 在日常生活中&#xff0c;要完成一个大任务&#xff0c;一般会将它分解成多个简单、容易解决的小问题&#xff0c;小问题逐个被解决&#xff0c;大问题也就随之解决了。 在多线程操作系统中&#xff0c;也同样需要开发人员把一个复杂的应用分解成多个小的、可调度的…

Ubantu安装mongodb,开启远程访问和认证

最近因为项目原因需要在阿里云服务器上部署MongoDB&#xff0c;操作系统为Ubuntu&#xff0c;网上查阅了一些资料&#xff0c;特此记录一下步骤。 1.运行apt-get install mongodb命令安装MongoDB服务&#xff08;如果提示找不到该package&#xff0c;说明apt-get的资源库版本比…

android 实现本地一键打包,告别繁琐的studio操作

前言 在实际开发项目中&#xff0c;我们的工程目录往往是多个app在一个工程下的&#xff0c;每次打包都需要手动的用studio点击Build->Generate Signed Bundle or APK->APK 选择app&#xff0c;签名等&#xff0c;甚至有的app签名还不一样&#xff0c;还需要手动的来回切…

iTOP-RK3588开发板Android12 设置系统默认不休眠

修改文件&#xff1a; device/rockchip/rk3588/overlay/frameworks/base/packages/SettingsProvider/res/values/defaults. xml 文件&#xff0c;如下图所示&#xff1a; - <integer name"def_screen_off_timeout">60000</integer> <integer name&q…

设计模式(一)

1、适配器模式 &#xff08;1&#xff09;概述 适配器中有一个适配器包装类Adapter&#xff0c;其包装的对象为适配者Adaptee&#xff0c;适配器作用就是将客户端请求转化为调用适配者中的接口&#xff1b;当调用适配器中的方法时&#xff0c;适配器内部会调用适配者类的方法…

【【萌新的STM32学习25--- USART寄存器的介绍】】

萌新的STM32学习25- USART寄存器的介绍 STM32–USART寄存器介绍&#xff08;F1&#xff09; 控制寄存器1 &#xff08;CR1&#xff09; 位13&#xff1a; 使能USART UE 0&#xff1a; USART分频器和输出被禁止 1&#xff1a; USART模块使能 位12 &#xff1a; 配置8个数据位…