【JavaEE】线程安全的集合类

引言

在Java标准库中,大部分集合类都是线程不安全的。Vector(比ArrayList多了同步化机制就变得线程安全了);Stack(继承Vector);Hashtable(只比Hashmap多了线程安全);以Concurrent开头的集合类:ConcurrentHashMap、ConcurrentLinkedQueue等;以CopyOnWrite开头的集合类:CopyOnWriteArrayList、CopyOnWriteArraySet等。以上这些都是线程安全的,其他的则不是线程安全。接下来分别介绍这些类的用法。队列

多线程下使用ArrayList

1. 手动加锁。使用synchronized、ReentrantLock。比如在一些方法上加锁等等。

2. 自动加锁。使用Collections.synchronizedList(new ArrayList<>())

                        Collections工具类里提供的这个工具相当于手动加锁的效果。

3. 使用CopyOnWrite。使用COW(写时拷贝),CopyOnWriteArrayList保证线程安全。

不同于上述两种方案,这种方法是如果有写操作,就把原来的集合先复制一份,在这个复制的集合上操作。操作完成后再把原来集合的引用指向刚才复制的。(以上操作是原子的)

优点:在读多写少的场景下,没有了锁竞争,效率更高

缺点:如果数据量很大,那么复制也是要消耗很多资源的。

           新写的数据不能第一时间读取到。

应用场景:这种读写分离的思想在服务器程序配置的维护上。

程序有很多的功能,这些功能开关我们可以放到配置文件上。如果不想要其中一个功能,那么可以在配置文件上关闭这个开关。一般配置文件修改完成后需要重启服务器才能生效,但是重启服务器会带来很大的损失。所以就会有”热加载“这个方法来解决。复制一份相同的配置文件,然后修改成目标文件;与此同时还是先用原来旧的文件,等修改完成后再使用新的即可。


多线程下使用哈希表

HashMap是线程不安全的,所以不考虑使用。

Hashtable是线程安全的,但是效率比较低,也不考虑使用。

效率低的原因:

1. 如果多个线程同时使用Hashtable,就直接造成锁冲突了。如下图。

 2. size属性也是通过synchronized来控制的,这是比较慢的。

3. 扩容的时候,是一次性全部调整完,把全部的元素拷贝到新的数组上的链表/红黑树上,那这次      扩容效率就极低。

ConcurrentedHashMap

线程安全的,效率也比较高。所以通常情况下我们使用这个类。接下来就解释以下为什么效率高。

效率高的原因(解决了Hashtable效率低的问题):

1. (JDK1.8情况下)给每个头节点都分别加上不同的锁。如下图所示:

而在JDK1.7及以前,则使用的是分段锁。如下图:

2. 使用了更多的CAS操作,比如size这个属性,避免出现了重量级锁。

3. 优化扩容方式。

        当放不下之后(超出负载因子设定值),就要进行扩容。

        ①先创作出来一个数组,把一部分的数据移过去(插入新值,删除旧值)。

        ②如果再次put时,在把一部分数据移过去。

        ③直到数据全部移除完成后,删除旧数组。

        ④在此期间如果要查询,两个数组的数据都要查;如果要删除,查询后找到删除即可。

4. 只对写操作加锁

读与读之间没有冲突;

读与写之间也没有冲突;(通过volatile和原子的写操作来精确控制不会发生脏读的情况)

写与写之间有冲突。


有什么错误评论区指出。希望可以帮到你。 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值