Java中线程安全的集合类

在先前的文章中我们已经讲过了原子类(线程安全的基本类型,基于CAS实现),详见常见锁策略,synchronized内部原理以及CAS-CSDN博客 ,我们在来讲一下集合类,在原来的集合类,大多数是线程不安全的,虽然vector,Stack,HashTable 是线程安全的,但由于其在一些关键方法上都加了synchronized,导致同时读以及单线程中也要加锁,于是java官方已经将其标为不安全类,不建议使用了,那我们如何在多线程下保证安全的使用一些集合类。

 多线程环境下使用ArrayList

1)自己使用同步机制(synchronized或者ReentrantLock)具体内容可以看这里Java线程安全问题以及解决方案-CSDN博客

2)使用Collections.synchronizedList()

List<String> synchronizedList = Collections.synchronizedList(new ArrayList<>());

用这种方式创建出的synchronizedList在关键方法上都具有synchronized

3)使用CopyOnWriteArrayList

CopyOnWriteArrayList 是 Java 中的一种并发集合类,它提供了一种在迭代时保证线程安全的机制。它的特点是在对集合进行修改(添加、删除元素)时,不直接在原始数据上进行操作,而是先将原始数据复制一份,然后在副本上进行修改,最后再将修改后的副本替换原始数据,且同时写时也会创建出两个拷贝。这种机制保证了在迭代过程中不会发生并发修改异常(ConcurrentModificationException),因为每次迭代都是在集合的一个固定的副本上进行的。

import java.util.concurrent.CopyOnWriteArrayList;

public class ConcurrentListExample {
    private static final int THREAD_COUNT = 3;
    private static final int OPERATIONS_PER_THREAD = 10000;

    private static CopyOnWriteArrayList<Integer> list = new CopyOnWriteArrayList<>();

    public static void main(String[] args) {
        // 创建并启动多个线程进行写操作
        for (int i = 0; i < THREAD_COUNT; i++) {
            Thread writerThread = new Thread(() -> {
                for (int j = 0; j < OPERATIONS_PER_THREAD; j++) {
                    list.add(j);
                }
            });
            writerThread.start();
        }

        // 创建并启动多个线程进行读操作
        for (int i = 0; i < THREAD_COUNT; i++) {
            Thread readerThread = new Thread(() -> {
                for (int j = 0; j < OPERATIONS_PER_THREAD; j++) {
                    int size = list.size(); // 读取列表大小
                    System.out.println("List size: " + size);
                    try {
                        Thread.sleep(10); // 模拟其他处理
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
            readerThread.start();
        }
    }
}

列表大小的变化:读线程会定期读取列表的大小并打印出来。由于写线程在不断地往列表中添加元素,因此列表的大小会逐渐增加。读线程每次读取到的列表大小可能会不同,取决于它在列表大小被修改之前或之后读取到的。读线程的竞争:由于多个读线程同时进行,它们可能会竞争访问 CopyOnWriteArrayList 的大小。因此输出结果中可能会出现多个读线程同时读取列表大小并打印的情况。写线程的竞争:多个写线程同时向 CopyOnWriteArrayList 中添加元素,它们之间也可能存在竞争。因此,列表中的元素可能会以不确定的顺序被添加。

如何安全地使用 CopyOnWriteArrayList

  1. 并发读写安全CopyOnWriteArrayList 在迭代时提供了线程安全的保证,因此可以安全地在多个线程中进行读操作和写操作,而无需额外的同步控制。

  2. 适用场景:适用于读操作远远多于写操作的场景,因为每次写操作都会触发一次数组的拷贝,可能会带来一定的性能开销。

  3. 实时性:需要注意,由于写操作会对原始数据进行拷贝,因此在写操作完成之前,迭代器可能会遍历到老的数据。如果需要实时性较高的结果,可能需要其他方式进行处理。

  4. 性能考虑:虽然 CopyOnWriteArrayList 提供了线程安全的迭代机制,但在写操作频繁的情况下,可能会产生较高的开销,因为每次写操作都需要拷贝整个数组。因此,对于写操作频繁的场景,可能需要考虑其他更合适的并发集合类。

总的来说,CopyOnWriteArrayList 是一种适用于读多写少的场景下保证线程安全的并发集合类,能够有效地解决在多线程环境中的并发访问问题。

多线程环境使用哈希表

HashMap 本身不是线程安全的,我们可以使用Hashtable, ConcurrentHashMap

Hashtable

  1. 线程安全性

    • Hashtable 是线程安全的,所有的公共方法都是同步的,因此可以在多线程环境中安全地使用。
  2. 性能

    • Hashtable 的所有方法都是同步的,而且是对整个HashTable加锁,这意味着在高并发的情况下可能会出现性能瓶颈。因为每次操作都需要获得锁来保证线程安全性。
    • Hashtable 使用一个称为扩容阈值(Expansion Threshold)的参数来控制何时需要进行扩容。当添加元素时,如果元素数量超过了当前容量乘以加载因子(Load Factor),就会触发扩容操作。默认情况下,加载因子是 0.75。扩容操作会创建一个新的数组,大小是原数组的两倍加一,然后将原有数据重新哈希到新数组中。因为 Hashtable 的所有方法都是同步的,所以在进行扩容时会锁定整个哈希表,可能会引起性能不稳定。

ConcurrentHashMap

  1. 线程安全性
    • ConcurrentHashMap 通过使用分段锁(Segment Locking)来实现线程安全性,它将整个存储空间分成多个段(Segment),每个段拥有自己的锁,这里的段在Java8以后可以简单理解成HashAMap的每一个链表或者树,锁对象则是链表头节点或者树的根节点,因此可以在大部分情况下实现更高的并发度。在Java 8及以后的版本中,ConcurrentHashMap 使用了 CAS (Compare and Swap) 操作来进一步提高性能,比如在size()方法上使用CAS实现了轻量级锁,避免了重量级锁的出现。
  2. 性能
    • 由于 ConcurrentHashMap 使用了分段锁和 CAS 操作,因此在高并发的情况下,通常比 Hashtable 的性能要好。它提供了更好的并发性能,更适合大规模并发访问的场景。
    • 在添加元素时,ConcurrentHashMap 在初始化时会创建一定数量的段,每个段内部都是一个独立的哈希表。当添加元素时,只会锁定对应段的锁,其他段的数据仍然可以被并发访问。
    • 扩容操作会在添加元素时自动触发,并且是分段进行的。每个段在达到一定的容量阈值时会触发扩容操作,而不是等待整个哈希表的容量达到阈值。
    • ConcurrentHashMap 的扩容是分段进行的,因此不会阻塞整个哈希表,可以在一定程度上提高并发性能。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ABAP - SALV教程11 红黄绿灯

SALV通过某列设置成异常列&#xff0c;SALV就会根据某列的值自动映射成红黄绿灯注意事项 该列的类型为CHAR1,即是结构的字段类型为CHAR1该字段的值赋值为 (space,1,2,3) space&#xff1a;灰灯、1&#xff1a;红灯、2&#xff1a;黄灯、3&#xff1a;绿灯 案例代码 CLASS lcl…

一份简单的前端开发指南

文章目录 一、HTML1、表格2、常见标签3、行内、块级4、行内块级元素 二、CSS1、三种样式2、链接样式3、浮动4、清除浮动5、伪类&#xff0c;伪元素6、position7、后代选择器8、弹性布局 三、JavaScripts1、null和undefined的区别2、var let const3、原生数据类型4、双等和三等5…

Qt 简约美观的加载动画 文本风格 第八季

今天和大家分享一个文本风格的加载动画, 有两类,其中一个可以设置文本内容和文本颜色,演示了两份. 共三个动画, 效果如下: 一共三个文件,可以直接编译 , 如果对您有所帮助的话 , 不要忘了点赞呢. //main.cpp #include "LoadingAnimWidget.h" #include <QApplic…

Github 2024-03-03 开源项目日报Top9

根据Github Trendings的统计&#xff0c;今日(2024-03-03统计)共有9个项目上榜。根据开发语言中项目的数量&#xff0c;汇总情况如下&#xff1a; 开发语言项目数量非开发语言项目4Rust项目1C项目1Jupyter Notebook项目1Python项目1Shell项目1 任天堂Switch模拟器yuzu&#x…

13-微服务初探-自研微服务框架

微服务初探 1. 架构变迁之路 1.1 单体架构 互联网早期&#xff0c;一般的网站应用流量较小&#xff0c;只需要一个应用&#xff0c;将所有的功能代码都部署在一起就可以&#xff0c;这样可以减少开发&#xff0c;部署和维护的成本。 比如说一个电商系统&#xff0c;里面包含…

【每日刷题】数组-LC56、LC238、随想录1、LC560

1. LC56 合并区间 题目链接 Arrays.sort先让intervals里的子数组按照子数组的第一个数字值从小到大排列。开一个新数组&#xff0c;newInterval&#xff0c;存放合并好的子数组让intervals的当前子数组i的第一个数字与newInterval的当前子数组index的最后一个数字比较大小&am…

2024 年适用于电脑的十大录制屏幕软件

录制屏幕软件的设计和开发旨在让您的工作流程更轻松、更高效。这些漂亮的工具有助于为教育目的捕获图像快照或记录屏幕以与客户共享模型。无论您寻找桌面屏幕录像机的原因是什么&#xff0c;这里都有最好的付费和免费实用程序。该类别中我们最喜欢的一些选择是 奇客录屏助手和 …

docker 转为docker-compose(composerize 命令)

可以使用Composerize将Docker命令转换为Docker Compose文件。 例如&#xff1a;将docker run命令转换为Docker Compose格式&#xff0c;只需用Composerize运行它&#xff0c;如下所示&#xff1a; composerize docker run -d -p 9000:9000 -v /var/run/docker.sock:/var/run/…

IDEA的安装教程

1、下载软件安装包 官网下载&#xff1a;https://www.jetbrains.com/idea/ 2、开始安装IDEA软件 解压安装包&#xff0c;找到对应的idea可执行文件&#xff0c;右键选择以管理员身份运行&#xff0c;执行安装操作 3、运行之后&#xff0c;点击NEXT&#xff0c;进入下一步 4、…

美团分布式 ID 框架 Leaf 介绍和使用

一、Leaf 在当今日益数字化的世界里&#xff0c;软件系统的开发已经成为了几乎所有行业的核心。然而&#xff0c;随着应用程序的规模不断扩大&#xff0c;以及对性能和可扩展性的需求不断增加&#xff0c;传统的软件架构和设计模式也在不断地面临挑战。其中一个主要挑战就是如…

凌特杯,第二届,数字音频传输。simulink matlab

终于比赛进入了尾声&#xff0c;最为指导老师也是非常的激动。接下来进入了论文写作阶段和视频拍摄阶段。 第二届凌特杯规定的硬件是ADI的Pluto&#xff0c;成本在2k以内&#xff0c;能支持MATLAB&#xff0c;它能够流畅的实时播放接收到的音乐数据&#xff0c;并把数据保存成…

图论例题解析

1.图论基础概念 概念 &#xff08;注意连通非连通情况&#xff0c;1节点&#xff09; 无向图&#xff1a; 度是边的两倍&#xff08;没有入度和出度的概念&#xff09; 1.完全图&#xff1a; 假设一个图有n个节点&#xff0c;那么任意两个节点都有边则为完全图 2.连通图&…

Mysql列子查询

目录 列子查询数据准备 列子查询 子查询返回的结果是一列(可以是多行)&#xff0c;这种子查询称为列子查询。 常用的操作符&#xff1a; 操作符描述IN在指定的集合范围之内&#xff0c;多选一NOT IN不在指定的集合范围之内 案例&#xff1a;查询"教研部"和"…

【UE 材质】冰冻效果

效果 步骤 1. 打开“Quixel Bridge” 下载冰的纹理 2. 新建一个材质&#xff0c;这里命名为“M_Frozen” 打开“M_Frozen”&#xff0c;添加如下节点&#xff0c;此时我们可以通过控制参数“偏移”来改变边界的偏移 此时预览效果如下 如果增加参数“偏移”的默认值效果如下 3.…

Transformer中的自注意力机制计算过程分析

目录 1 什么是自注意力机制 2 自注意力的计算过程 1 什么是自注意力机制 自注意力机制&#xff08;Self-Attention&#xff09;顾名思义就是关注单个序列内部元素之间的相关性&#xff0c;不仅可以用于 seq2seq 的机器翻译模型&#xff0c;还能用于情感分析、内容提取等场景…

Doris实战——美联物业数仓

目录 一、背景 1.1 企业背景 1.2 面临的问题 二、早期架构 三、新数仓架构 3.1 技术选型 3.2 运行架构 3.2.1 数据模型 纵向分域 横向分层 数据同步策略 3.2.2 数据同步策略 增量策略 全量策略 四、应用实践 4.1 业务模型 4.2 具体应用 五、实践经验 5.1 数据…

Spring Boot项目中不使用@RequestMapping相关注解,如何动态发布自定义URL路径

一、前言 在Spring Boot项目开发过程中&#xff0c;对于接口API发布URL访问路径&#xff0c;一般都是在类上标识RestController或者Controller注解&#xff0c;然后在方法上标识RequestMapping相关注解&#xff0c;比如&#xff1a;PostMapping、GetMapping注解&#xff0c;通…

Java项目:30 基于SpringBoot自习室座位预定系统

作者主页&#xff1a;源码空间codegym 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文中获取源码 项目介绍 功能设计 管理员 1、用户管理 管理员可以新增、删除管理员 管理员可以删除学生 2、自习室管理 管理员可以新增自习室、设置自习室的座位…

关于福彩历史数据采集器和体彩历史数据采集器的下载安装说明

前段时间因为研究基于人工神经网络&#xff08;深度学习&#xff0c;所谓的“AI”算法&#xff09;对3D开奖数据进行预测&#xff0c;开发了两款浏览器插件----“福彩历史数据采集器”和“体彩历史数据采集器”。之所以开发这两款插件&#xff0c;是因为不管是基于什么样的方式…

机器学习:集成学习(Python)

一、Adaboost算法 1.1 Adaboost分类算法 adaboost_discrete_c.py import numpy as np import copy from ch4.decision_tree_C import DecisionTreeClassifierclass AdaBoostClassifier:"""adaboost分类算法&#xff1a;既可以做二分类、也可以做多分类&#…
最新文章