当前位置: 首页 > article >正文

Java中的集合类与线程安全的讨论

1.ArrayList

ArrayList是线程不安全的,可以在单线程中使用,在多线程中可以根据代码选择合适的时机进行加锁,实现线程安全的操作,但对代码能力要求较高。

2.Collections.synchronizedList(new ArrayList)

返回的List中的关键操作上都带有synchronized,但由于加锁较多,使得锁冲突发生的效率较高,代码阻塞的概率较大,对性能会有一定的影响。

3.CopyOnWriteArrayList

CopyOnWrite即写时拷贝,当我们在修改变量的同时进行变量的读取时,使用写时拷贝会降低线程安全问题;

现有如下需求,将arrayList1中的值进行修改,

 

当我们进行简单的修改操作并同时进行数据的读取,这时就会出现下面的结果:100, 200,  3,4,5,这样的结果显然不是我们向读到的,这属于半修改状态的数据,于是可以使用写时拷贝。

现有一个引用指向arrayList1,当我们进行数据的修改时,就会创建出另一个新的顺序表arrayList2,在将新的数据写入到arrayList2中时,若需要进行读数据操作,这时就会只读取arrayList1中的数据,这样读到的数据就会全部都是旧的数据,这样就不会出现读取到半新半旧的数据,当数据全部写入完后,就会将引用指向arrayList2,这样就完成了多线程下的数据修改操作,并且不会引发线程安全问题。

虽然数据的复制与更新不是原子的,但由于在修改完成之前保存了旧的数据,就不会影响数据的读取;当数据修改完成后,引用的赋值时原子的,,也不会引发线程安全问题。

但上述方案也会有明显的问题:

1)当数据量很大时,拷贝的时间就会非常长,效率低下;

2)当有多个线程进行数据的修改操作时,也会引发线程安全问题,因为在修改完成后引用指向哪个修改完的顺序表是不确定的,这样就会导致修改的数据可能达不到自己想要的状态,而上述的写时拷贝操作也仅仅是两个线程之间的操作,一个读一个修改。

4.阻塞队列

ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue均是线程安全的。

5.HashMap

HashMap在不涉及线程安全的操作时效率非常高,但无法避免线程安全问题。

6.Hashtable

Hashtable是线程安全的,但由于他的每一个public方法均使用synchronized修饰,并且只有一把锁,导致容易发生锁冲突,导致线程阻塞的概率大,效率较低。

7.ConcurrentHasshMap

这个是线程安全的,并且针对桶级别进行加锁,效率更高。

对于一般的哈希表,当发生哈希冲突时,有两种解决方案:

1)线性探测,但是这种方法效率较低,不推荐使用;

2)链表,将会引发哈希冲突的数据放到链表上,效率更高,推荐使用,如下图:

对于HashMap,在多线程操作中当我们涉及到以下操作时会引发线程安全问题:

1)修改一个链表上的同一个值;

2)修改同一个链表上的两个元素,如在一个数后插入两个元素,一个线程插入一个;

3)修改HashMap的size等......

于是我们可以进行加锁操作,但不能向Hashtable一样,只是用一个锁并且将所有的public方法加上synchronized,我们可以针对每一个链表进行加锁操作,每一个链表使用不同的锁对象,这样就能有效地减少所冲突地概率,并且发生上述地1、2情况时会避免线程安全问题,进行线程阻塞;

但由于链表的个数可能会很多,导致锁对象地开销较大,在java中,锁对象可以是任意对象,于是可以使用每一个链表的头节点作为锁对象,这样就会减少锁对象的空间消耗。

但对于情况3,可以对size单独加锁,但这种解决方式不是最好的,我们可以使用原子类来代替size,即创建AtomicInteger对象来代表size,代码如下:

AtomicInteger size = new AtomicInteger(0); //0为初始值

size.getAndIncrement(); //相当于size++

size.incrementAndGet(); //相当于++size

szie.addAndGet(n); //相当于size+=n

上述的三个针对size的操作均是原子的,是线程安全的;

对ConcurrentHasshMap进行扩容时,需要将原有的数据复制到新的哈希表中,当数据量较大时,一次复制所有的数据所需要的时间和空间开销都非常大,效率低下,于是可以分批次复制,一次只复制所有数据的百分之几,之后的每一次操作都会触发复制操作,几次之后就能完成数据的复制,这样的效率更高。


http://www.kler.cn/a/395770.html

相关文章:

  • js中typeOf无法区分数组对象
  • 万字长文解读深度学习——生成对抗网络GAN
  • ABAP关于PS模块CJ20N中项目物料的屏幕和字段增强CI_RSADD
  • 硬件工程师之电子元器件—二极管(4)之热量对二极管温度特性的影响
  • uniapp在app模式下组件传值
  • 三、损失函数
  • ETLCloud支持的数据处理类型包括哪些?
  • ubuntu docker里面安装Omniverse Launcher不能登陆
  • 【Elasticsearch】01-ES安装
  • node对接ChatGpt的流式输出的配置
  • Apache Doris:深度优化与最佳实践
  • Dev C++ 无法使用to_string方法的解决
  • shell编程(2)永久环境变量和字符串显位
  • 利用云计算实现高效的数据备份与恢复策略
  • 使用 DBSCAN(基于密度的聚类算法) 对二维数据进行聚类分析
  • Spring基础之——控制反转(IOC)、依赖注入(DI)与切面编程(AOP)概念详解(适合小白,初学者必看)
  • 问:数据库的六种锁机制实践总结?
  • C语言,用最小二乘法实现一个回归模型
  • (附项目源码)Java开发语言,211 springboot 在线问诊系统的设计与实现,计算机毕设程序开发+文案(LW+PPT)
  • 谷歌Gemini发布iOS版App,live语音聊天免费用!
  • 基于微信小程序的乡村研学游平台设计与实现,LW+源码+讲解
  • 科锐国际,蓝禾,汤臣倍健,三七互娱,GE医疗,得物,顺丰,快手,途游游戏25秋招内推
  • 14天Java基础学习——第6天:面向对象编程(类与对象)
  • 实验1-1 顺序表的基本操作
  • ceph的集群管理
  • 计算机的错误计算(一百五十五)