【多线程】多线程下使用集合类

一.Java中线程安全的集合类

Java中提供了多种线程安全的集合类,它们可以在多线程环境下安全地被多个线程同时访问而无需外部同步。以下是一些常见的线程安全集合类:

  1. Vector: 类似于 ArrayList 的动态数组,但是所有方法都是同步的,提供了基本的线程安全保证。
  2. Hashtable: 是一个线程安全的哈希表实现,与 HashMap 类似,但所有操作都是同步的。
  3. Stack: 继承自 Vector 的线程安全栈实现。
  4. ConcurrentLinkedQueue: 使用非阻塞算法的线程安全队列实现。
  5. BlockingQueue: 这是一个接口,它有多个实现类(如 ArrayBlockingQueue, LinkedBlockingQueue 等),这些实现类提供了线程安全的队列操作。
  6. CopyOnWriteArrayList: 一个线程安全的列表,它在修改操作时复制底层数组,从而避免并发修改异常。
  7. CopyOnWriteArraySet: 基于 CopyOnWriteArrayList 的线程安全集合。
  8. ConcurrentHashMap: 提供比 Hashtable 更好的并发性能的线程安全哈希表实现。
  9. ConcurrentSkipListMap: 线程安全且数据有序的哈希表。

需要注意的是,虽然这些集合类是线程安全的,但在特定场景下可能存在性能差异。例如,Vector和Hashtable的每个操作都是通过synchronized关键字进行同步的,这可能在高并发情况下成为性能瓶颈。而其他一些类,如ConcurrentHashMap,采用了更精细的锁定机制来提高并发性能。

二.多线程下使用ArrayList

  • 给自己加锁 : 在涉及线程安全的代码中用(synchronized或者ReentrantLock)加锁
  • 使用Collections.synchronizedList包装器:Collections.synchronizedList(new ArrayList ) 给ArrayList各种操作本身不带锁.通过上述套壳之后,得到的新的对象里面的关键方法都带有锁了.
    Java 提供了一个便利的方法来创建一个同步的列表,即 Collections.synchronizedList。这个方法返回一个线程安全的列表,所有对它的直接操作都是同步的。但请注意,迭代时用户必须手动对这个列表进行同步。
    代码示例:
import java.util.Collections;
import java.util.List;
import java.util.ArrayList;List<Object> syncList = Collections.synchronizedList(new ArrayList<>());public void addToList(Object item) {synchronized(syncList) {syncList.add(item);}
}public Object removeFromList(int index) {synchronized(syncList) {return syncList.remove(index);}
}
  • 使用并发集合类:CopyOnWriteArrayList 写时拷贝,多个线程读是没有线程安全问题的 如果多个线程读这个顺序表, 没有任何线程安全问题~如果一旦有线程要修改这里的值,就会把该顺序表复制一份,修改复制表中的内容,然后修改引用的指向(原子的)
    如果性能是个问题,并且你不想手动管理同步,可以考虑使用 java.util.concurrent 包中的线程安全集合类,如 CopyOnWriteArrayList。
    代码示例:
import java.util.concurrent.CopyOnWriteArrayList;List<Object> cowList = new CopyOnWriteArrayList<>();public void addToList(Object item) {cowList.add(item);
}public Object removeFromList(int index) {return cowList.remove(index);
}

三.多线程下使用队列

多线程下使用队列一般是使用阻塞队列:
Java 提供了多种阻塞队列的实现,它们可以在多线程环境下安全地传递元素。以下是一些常用的阻塞队列种类:

  1. ArrayBlockingQueue: 一个由数组支持的有界阻塞队列。
  2. LinkedBlockingQueue: 一个由链表支持的可选边界阻塞队列,其性能通常优于 ArrayBlockingQueue。
  3. PriorityBlockingQueue: 一个支持优先级排序的无界阻塞队列。
  4. DelayQueue: 一个使用优先级队列实现的无界阻塞队列,用于延迟元素的传输。
  5. SynchronousQueue: 一个不存储元素的阻塞队列,适用于传递性设计。
  6. LinkedTransferQueue: 一个由链表支持的无界阻塞队列,相对于 LinkedBlockingQueue,它提供了更高级的传输操作。TransferQueue 的应用场景是,当不想生产者过度生产消息时,TransferQueue可能非常有用,在这样的设计中,消费者的消费能力将决定生产者产生消息的速度。
  7. LinkedBlockingDeque: 一个由双向链表支持的可选边界阻塞队列,可以用作栈或队列。
  8. ConcurrentLinkedQueue: 虽然不是阻塞队列,但它是 Java 中的一个非阻塞线程安全的队列,适用于高并发场景。

这些队列提供了不同的特性和性能特点,适用于不同的应用场景。例如,如果你需要处理具有优先级的任务,可以使用 PriorityBlockingQueue。如果你需要一个不存储任何元素而是直接将生产的元素传递给消费者的队列,那么 SynchronousQueue 可能是合适的选择。

在选择阻塞队列时,需要考虑队列的特性,如是否有界、是否支持优先级、是否支持延迟处理等。此外,还需要考虑预期的使用模式,例如,是否会有多个生产者和消费者,以及它们之间的交互模式。了解这些队列的特点和适用场景,可以帮助你更好地在多线程程序中管理数据的生产和消费。

四.多线程下使用哈希表

HashMap肯定不行,是线程不安全的
HashTable 给HashMap的关键方法加锁来保证线程安全
Hashtable的每个操作都是通过synchronized关键字进行同步的,这可能在高并发情况下成为性能瓶颈.因此就引出了ConcurrentHashMap(并发HashMap)

ConcurrentHashMap的优化:

  • 1.最大的优化之处减小了锁的粒度 HashMap由数组+若干的链表 . 若是HashTable若修改两个不同的链表中的数据,也会产生锁竞争,这也相当于给this加锁了;缩小了锁的粒度,就相当于把锁给到了每一个链表,此时若再修改两个不同链表中的值,就不会产生锁冲突了 ConcurrentHashMap相比于HashTable 大大缩小了锁冲突的概率,把一把大锁,转换成多把小锁了。
    在这里插入图片描述
    此时元素1和元素2在同一链表上,如果线程A修改(增删)元素1,线程B修改元素2,那么此时是有线程安全问题的(相邻两元素并发的插入或者删除的时候,相邻两节点的next指向可能会发生改变)。如果线程A修改(增或者删)元素3,线程B修改元素4,,这个情况相当于多个线程修改不同的变量,那么此时是没有线程安全问题的。
    使用HashTable,锁冲突概率就太大了,任何两个元素的操作都会有锁冲突,即使是处在不同的链表上,这就是不用HashTable的主要原因。
    ConcurrentHashMap做法是,每个链表有各自的锁(而不是大家共用同一个锁了),具体来说,就是使用每个链表的头结点,作为锁对象(两个线程针对同一个锁对象加锁才有锁竞争,才有阻塞等待,针对不同对象,没有锁竞争)。
    此时,把锁的粒度变小了,针对12这个情况,是针对同一把锁进行加锁,会有锁竞争,会保证线程安全。针对34这个情况,是针对不同的锁进行加锁,不会有锁竞争了,没有阻塞等待,程序就会更快。
  • 2.针对读操作,不加锁,只针对写操作加锁
    读和读之间没有冲突;写和写之间有冲突,可以加锁;读和写之间没有冲突,但是很多场景下,读写之间不加锁控制,如果写操作不是原子的,那么会产生脏读,所以使用了 volatile 保证了原子性。
  • 3.充分的使用了CAS原子操作~减少了一些加锁 -比如针对哈希表元素个数的维护
  • 4.针对扩容操作的优化:
    扩容本质上是创建一个更大的数组,把旧的Hash表中的元素都给搬运到新的数组上,如果Hash表的元素本身非常多,这里的扩容操作就会消耗很长时间.
    扩容优化:HashMap的扩容操作是一把梭哈 . 在某次插入元素的操作中,整体完成扩容…而ConcurrentHashMap则是每次操作都只是搬运一部分元素~~就是在扩容的过程中,同时存在两份哈希表,一份是旧的,一份是新的.插入操作,直接往新的上插,删除操作,新的旧的都删除,查找操作新的和旧的都需要查询.
    小结:
一.Hashtable和HashMap、ConcurrentHashMap 之间的区别?
  • HashMap: 线程不安全. key 允许为 null
  • Hashtable: 线程安全,使用 synchronized 锁 Hashtable 对象, 效率较低,key 不允许为 null
  • ConcurrentHashMap: 线程安全,使用 synchronized 锁每个链表头结点, 锁冲突概率低, 充分利用
  • CAS 机制,优化了扩容方式,key 不允许为 null
二. ConcurrentHashMap在jdk1.8做了哪些优化?
  • 取消了分段锁, 直接给每个哈希桶(每个链表)分配了一个锁(就是以每个链表的头结点对象作为锁对象)。将原来 数组 + 链表 的实现方式改进成 数组 + 链表 / 红黑树 的方式,当链表较长的时候(大于等于8 个元素)就转换成红黑树。
  • PS:分段锁是 Java1.7 中采取的技术,Java1.8 中已经不再使用了,简单的说就是把若干个哈希桶分成一个"段", 针对每个段分别加锁,目的也是为了降低锁竞争的概率。当两个线程访问的数据恰好在同一个段上的时候, 才触发锁竞争。
三.ConcurrentHashMap的读是否要加锁?

读操作没有加锁,目的是为了进一步降低锁冲突的概率,为了解决脏读,保证读到刚修改的数据, 搭配了

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

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

相关文章

lottery-攻防世界

题目 flag在这里要用钱买&#xff0c;这是个赌博网站。注册个账号&#xff0c;然后输入七位数字&#xff0c;中奖会得到相应奖励。 githacker获取网站源码 &#xff0c;但是找到了flag文件但是没用。 bp 抓包发现api.php&#xff0c;并且出现我们的输入数字。 根据题目给的附…

跨越网络边界:借助C++编写的下载器程序,轻松获取Amazon商品信息

背景介绍 在数字化时代&#xff0c;数据是新的石油。企业和开发者都在寻找高效的方法来收集和分析网络上的信息。亚马逊&#xff0c;作为全球最大的电子商务平台之一&#xff0c;拥有丰富的商品信息&#xff0c;这对于市场分析和竞争情报来说是一个宝贵的资源。 问题陈述 然…

Vue - 你知道Vue中computed和watch的区别吗

难度级别:中高级及以上 提问概率:70% 二者都是用来监听数据变化的,而且在日常工作中大部分时候都只是局限于简单实用,所以到了面试中很难全面说出二者的区别。接下来我们看一下,二者究竟有哪些区别呢? 先说computed,它的主要用途是监听…

vs2017离线安装(配合QT5.9.2使用)

以vs2017_Professional版本为例&#xff1a; 一、下载安装包vs2017_Professional.exe&#xff08;在线安装包即可&#xff09; 二、创建在目录&#xff1a;C:\vs2017_Professional_Package&#xff0c;把vs2017_Professional.exe放在该目录下。 ID&#xff1a; Microsoft.Vis…

C++设计模式:桥模式(五)

1、定义与动机 桥模式定义&#xff1a;将抽象部分&#xff08;业务功能&#xff09;与实现部分&#xff08;平台实现&#xff09;分离&#xff0c;使他们可以独立地变化引入动机&#xff1a; 由于某些类型的固有的实现逻辑&#xff0c;使得它们具有两个变化的维度&#xff0c;…

QT 使用redis ,连接并使用

一.redis安装 链接&#xff1a;https://pan.baidu.com/s/17fXKOj5M4VIypR0y5_xtHw 提取码&#xff1a;1234 1.下载得到文件夹如图 course_redis为安装包。 2.启动Redis服务 把安装包解压到某个路径下即可。 打开cmd窗口&#xff0c;切换到Redis安装路径&#xff0c;输入 r…

4月21日,Sui成都开发者茶话会诚邀您来

由 Sui Foundation, 开发者教育平台 HackQuest&#xff0c;MoveBit&#xff0c;PoP Planet 共同主办的 Sui 成都开发者茶话会将于 4 月 21 日下午 2:00–6:00 举办&#xff0c;我们诚挚邀请所有对 Sui 生态&#xff0c;Sui Move 语言&#xff0c;和 Web3 开发有兴趣的小伙伴前来…

基于Springboot框架北京某大学失物招领系统设计与实现 研究背景和意义、国内外现状_失物招领发展现状

此外&#xff0c;基于Springboot框架的北京某大学失物招领系统还具有重要的社会意义。该系统可以为广大师生提供更加便捷、高效的失物招领服务&#xff0c;减少因失物而带来的经济损失和精神困扰。同时&#xff0c;该系统还可以促进校园文化的建设和传播&#xff0c;营造和谐、…

【UE 网络】DS框架学习路线

目录 0 引言1 如何学习DS框架1. 熟悉Unreal Engine基础2. 学习网络编程基础3. 掌握UE网络概念4. 实践和实验5. 加入社区和论坛6. 官方示例和案例研究7. 专业书籍和在线课程 2 DS框架重要知识点有哪些1. 网络复制2. 远程过程调用&#xff08;RPC&#xff09;3. 客户端服务器架构…

用函数指针写两个操作数的相关运算

文章目录 概要整体架构流程代码实现小结 概要 我们以加&#xff0c;减&#xff0c;乘&#xff0c;除为例来示范 整体架构流程 首先我们先实现一个菜单功能来进行选择&#xff1a;把他封装成一个menu函数 然后把加减乘除分别用不同的函数实现 为了选择我们选择使用switch来…

python实现OCR:pytesseract和pyddleocr(附代码)

文章目录 背景pytesseractpaddleocr百度apipaddleocr 背景 OCR是光学字符识别&#xff08;Optical Character Recognition&#xff09;的缩写&#xff0c;通过扫描等光学输入方式和文字识别将图片中的文字提取出来&#xff0c;非常适用于提取网络截图或扫描pdf等文件里的文本。…

JSON字符串中获取一个特定字段的值

JSON字符串中获取一个特定字段的值 一、方式一&#xff0c;引用gson工具二、方式二&#xff0c;使用jackson三、方式三&#xff0c;使用jackson转换Object四、方式四&#xff0c;使用hutool&#xff0c;获取报文数组数据 一、方式一&#xff0c;引用gson工具 测试报文&#xf…