java的map线程安全吗_Java中Map线程安全实现

前言介绍:volatile和transient关键字

第一种实现:HashTable

第二种实现:Collections.synchronizedMap(map)

第三种实现:ConcurrentHashMap

前言介绍:volatile和transient关键字

volatile(易变的,不稳定的),作用:

volatile变量写入时:JVM会把该线程对应的本地内存私有拷贝变化强制刷新到主内存中去,并且会其他线程中的本地内存私有拷贝无效;

volatile变量读取时:强迫从主内存中重读该成员变量的值;JVM为了获得最佳速度,允许每个线程保存共享成员变量的私有拷贝,而且只当线程进入或者离开同步代码块时才与共享成员变量的原始值对比(如下图)。volatile关键字就是告诉JVM,不要优化了,我是易变的,是在多线程中访问的成员变量,如果是synchronized代码块中变量,或者最终常量,那么就不需要volatile

49463c9bd90a0e115edd5eb50307af9e.png

根据1,2两点特性,volatile变量控制多个线程读写,同时操作主存,是不是绝对线程安全了呐?答案是NO,可以肯定如果是 volatile int count = 1; 在多个线程中操作count = 2;这种原子操作是线程安全的;相反:一些读写复合操作 count++,会先读取count,然后加一,最后赋值count。只依靠volatile就不行了,AtomicInteger是采用CAS(compare and swap)机制,实现无锁线程安全。

禁止指令重排序优化

首先指令重排序优化是什么?

重排序是对代码指令的执行顺序重拍:比如说,a=1;b=3;,JVM可能会重排序为先b=3;a=1;如果fun和run并发执行,最终b可能是2或者3

7f0352711eec3c056a70c496703b0afb.png

transient(短暂的,临时的),作用:

先说说Serializable接口是赋予对象可序列化和反序列化的。transient修饰的变量,序列化到文件中对应的值为NOT SET

第一种实现:HashTable

核心思想synchronized修饰所有方法体

从put方法可以看出,不允许key,value为空

089377871d3fd0556a2e92e9470d35ba.png

287b58f19f347f19ddd54448fe4f15b1.png

第二种实现:Collections.synchronizedMap(map)

源码发现,核心点在于final Map m;是一个指针指向传入的map参数,并且这个指针是不可改变的,如果后续令map = null;并不影响这个final变量的指向;Object mutex(互斥体)使用synchronized获取这个互斥体变量的占有,确保了多线程安全

59e0e9276e185a8e6e691ad306be31bd.png

下图注释1操作是把map1指针指向的Map对象清空,syncmap1指向的Map对象也清空了,所以去除是null;注释1操作是把map2指针清空,并不影响syncmap2已经记录了指向的Map对象

7c1fd9d0700a3cd608edffc8f294a9ea.png

第一种和第二种实现在put和get会争夺锁,如果大量读取和写入并不在同一段?那么就引出了第三种实现

第三种实现:ConcurrentHashMap

JDK7容器里有多把锁,每一把锁用于锁容器其中一部分数据,那么当多线程访问容器里不同数据段的数据时,线程间就不会存在锁竞争,从而可以有效的提高并发访问效率,这就是ConcurrentHashMap所使用的锁分段技术,首先将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。有些方法需要跨段,比如size()和containsValue(),它们可能需要锁定整个表而而不仅仅是某个段,这需要按顺序锁定所有段,操作完毕后,又按顺序释放所有段的锁。这里“按顺序”是很重要的,否则极有可能出现死锁

179829960ea402b96fe77c6bc1a7834f.png

JDK8实现已经摒弃了Segment的概念,而是直接用Node数组+链表+红黑树的数据结构来实现,并发控制使用synchronized和CAS来操作,整个看起来就像是优化过且线程安全的HashMap

因为锁力度降低到单个node,所以使用synchronized来控制锁,synchronized关键字也是官方一直在JVM底层持续优化,效率更高

0db7b0617b96c3c19abc43b354988963.png

val和next都会在扩容时发生变化,所以加上volatile来保持可见性和禁止重排序

4e6c994f57b027562d0b365164535634.png

3811dcacc839ce5bda864238b285e8b5.png

弱化后的Segment:

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值