多线程与高并发(13)——Java常见并发容器总结

news/2024/7/7 18:58:47

本文总结常见的并发容器,包含ConcurrentHashMap、CopyOnWriteArrayList 、ConcurrentLinkedQueue、BlockingQueue 、ConcurrentSkipListMap,本文仅做简单的总结,不做详细的源码分析。

一、ConcurrentHashMap

HashMap不是线程安全的,ConcurrentHashMap是线程安全的。
ConcurrentHashMap的数组结构也是Node 数组 + 链表 / 红黑树,同时它采用的Synchronized 锁加CAS的机制,引用了锁升级的策略,所以性能方面没有太多问题。
在进行读操作时(几乎)不需要加锁,而在写操作时通过锁分段技术只对所操作的段加锁而不影响客户端对其它段的访问。
这里不过多总结了,回头单独写一篇文章,从源码理解ConcurrentHashMap。

二、CopyOnWriteArrayList

CopyOnWriteArrayList 是一个线程安全且读操作无锁的ArrayList ,ArrayList 是什么呢,查询速度很快,有序且可重复,底层是一个数组。

public class CopyOnWriteArrayList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable {

由于日常使用时,读操作远远大于写操作,类似于ReentrantReadWriteLock(读写锁),为了进一步增加读取的效率,这里直接就不给读操作加锁,而且:写入也不会阻塞读取操作。只有写入和写入之间需要进行同步等待。
我们先看读操作源码:

@SuppressWarnings("unchecked")
    private E get(Object[] a, int index) {
        return (E) a[index];
    }

    public E get(int index) {
        return get(getArray(), index);
    }
    final Object[] getArray() {
        return array;
    }

嗯,平平无奇,就是正常获取数组的值。
再看写操作的代码,我们看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();
        }
    }

CopyOnWriteArrayList 我们看名字就知道,在写操作时,进行复制。在复制的副本中进行操作,然后再替换原来的数据,这样就不会阻碍读操作了。上面源码中也是,先加锁,防止复制多个副本,然后操作副本,然后写回原处。
从计算机的角度来看:复制旧内存块,改写新内存,然后指针指向新内存,回收就内存。

三、ConcurrentLinkedQueue

ConcurrentLinkedQueue是非阻塞队列,主要使用 CAS 非阻塞算法来实现线程安全。
从名字可以看出,ConcurrentLinkedQueue使用链表作为其数据结构,ConcurrentLinkedQueue 应该算是在高并发环境中性能最好的队列了。
这里不作过多的解读了,后续有时间专门出一篇源码总结。

四、BlockingQueue

BlockingQueue是阻塞队列。 BlockingQueue 是一个先进先出的队列(Queue),为什么说是阻塞(Blocking)的呢?是因为 BlockingQueue 支持当获取队列元素但是队列为空时,会阻塞等待队列中有元素再返回;也支持添加元素时,如果队列已满,那么等到队列可以放入新元素时再放入。
BlockingQueue 在生产者-消费者的场景中,是支持多消费者和多生产者的,说的其实就是线程安全问题。
BlockingQueue常用的实现类有:ArrayBlockingQueue、LinkedBlockingQueue 、PriorityBlockingQueue 。

ArrayBlockingQueue 一旦创建,容量不能改变。其并发控制采用可重入锁 ReentrantLock,不管是插入操作还是读取操作,都需要获取到锁才能进行操作。当队列容量满时,尝试将元素放入队列将导致操作阻塞;尝试从一个空队列中取一个元素也会同样阻塞。
ArrayBlockingQueue 默认情况下不能保证线程访问队列的公平性,所谓公平性是指严格按照线程等待的绝对时间顺序,即最先等待的线程能够最先访问到 ArrayBlockingQueue。而非公平性则是指访问 ArrayBlockingQueue 的顺序不是遵守严格的时间顺序,有可能存在,当 ArrayBlockingQueue 可以被访问时,长时间阻塞的线程依然无法访问到ArrayBlockingQueue。

若想保证公平性,采用代码如下:

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

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

PriorityBlockingQueue 是一个支持优先级的无界阻塞队列。默认情况下元素采用自然顺序进行排序,也可以通过自定义类实现 compareTo() 方法来指定元素排序规则,或者初始化时通过构造器参数 Comparator 来指定排序规则。
PriorityBlockingQueue 并发控制采用的是可重入锁 ReentrantLock,队列为无界队列(ArrayBlockingQueue 是有界队列LinkedBlockingQueue 也可以通过在构造函数中传入 capacity 指定队列最大的容量,但是 PriorityBlockingQueue 只能指定初始的队列大小,后面插入元素的时候,如果空间不够的话会自动扩容)。

五、ConcurrentSkipListMap

ConcurrentSkipListMap表示使用跳表实现map。跳表是什么呢,看下图(图片引用:https://javaguide.cn/java/concurrent/java-concurrent-collections.html#priorityblockingqueue):
在这里插入图片描述
跳表的本质是同时维护了多个链表,并且链表是分层的,最底层是所有元素的链表,每上面一层链表都是下面一层的子集。跳表内的所有链表的元素都是排序的。查找时,可以从顶级链表开始找。一旦发现被查找的元素大于当前链表中的取值,就会转入下一层链表继续找。这也就是说在查找过程中,搜索是跳跃式的。
如果查18的话,本来要查18次,现在只需要查1,7,13,13,17,17,18这7个就行了。
跳表是一种利用空间换时间的算法。
使用跳表实现 Map 和使用哈希算法实现 Map 的另外一个不同之处是:哈希并不会保存元素的顺序,而跳表内所有的元素都是排序的。因此在对跳表进行遍历时,你会得到一个有序的结果。
本文参考:
https://javaguide.cn/java/concurrent/java-concurrent-collections.html#concurrentskiplistmap


http://lihuaxi.xjx100.cn/news/207741.html

相关文章

2023年天津市大学软件学院专升本专业课报名缴费考试的通知

天津市大学软件学院联合招生2023年“高职升本科”专业课报名缴费、打印准考证和考试时间的通告 根据《2023年天津市高职升本科招生实施办法》&#xff08;津招办高发〔2022〕15号&#xff09;通知的相关要求&#xff0c;做好联合招生专业考试相关工作。为全面服务考生&#xff…

深度学习快速入门----Pytorch 系列3

注&#xff1a;参考B站‘小土堆’视频教程 视频链接&#xff1a;【PyTorch深度学习快速入门教程&#xff08;绝对通俗易懂&#xff01;&#xff09;【小土堆】 系列文章&#xff1a; 深度学习快速入门----Pytorch 系列1 深度学习快速入门----Pytorch 系列2 深度学习快速入门--…

Python小课之数据类型初识

Python支持的六大数据类型&#xff1a; 今天带大家了解下python的数据类型&#xff0c;以python3为主&#xff0c;有六大数据类型 Number&#xff08;数字&#xff09;-不可变String&#xff08;字符串&#xff09;-不可变List&#xff08;列表&#xff09;-可变Tuple&#xff…

Maven 高级篇,Maven常用操作、高级操作、nexus私服搭建

&#x1f600;&#x1f600;&#x1f600;创作不易&#xff0c;各位看官点赞收藏. Maven 高级篇 文章目录Maven 高级篇1、Maven 安装2、Maven 核心概念2.1、坐标2.2、基础操作2.3、依赖2.4、继承2.5、生命周期3、Maven 深入3.1、Spring Boot 打包3.2、超级 POM3.3、build 标签…

java毕业设计电商项目mybatis+源码+调试部署+系统+数据库+lw

java毕业设计电商项目mybatis源码调试部署系统数据库lw java毕业设计电商项目mybatis源码调试部署系统数据库lw本源码技术栈&#xff1a; 项目架构&#xff1a;B/S架构 开发语言&#xff1a;Java语言 开发软件&#xff1a;idea eclipse 前端技术&#xff1a;Layui、HTML、C…

第151篇 Solidity Example(1)

目录 1.Hello World 2.First Application 3.Primitive Data Types 4.Variables 5.Constants 6.Immutable 7.Reading and Writing to a State Variable 8.Ether and Wei

Linux软件管理之RPM的五种操作模式—这篇总结你一定能读懂

Linux软件管理之RPM的五种操作模式—这篇总结你一定能读懂Linux常用软件包类型RPM软件包管理1.安装RPM包2.卸载RPM包3.查询RPM包4.升级RPM包5.验证RPM包前言 ​ Hello啊&#xff0c;码友们。Today&#xff0c;我们将一起来学习一下Linux操作系统中的软件管理部分。本次更新&…

Linux21 --- 计算机网络基础概论(网络基本概念、网络分层模型、网络应用程序通信流程)

一、网络基本概念 1、网络 网络是由若干结点和连接这些结点的链路组成&#xff0c;网络中的结点可以是计算机&#xff0c;交换机、 路由器等设备。 网络设备有&#xff1a;交换机、路由器、集线器 传输介质有&#xff1a;双绞线、同轴电缆、光纤 下图是一个简单的网络示意图…