concurrenthashmap读加锁吗,concurrenthashmap18锁

http://www.itjxue.com  2023-01-05 13:49  来源:未知  点击次数: 

concurrentmap为什么线程安全

concurrentmap线程安全原因:

1、JDK1.7中,ConcurrentHashMap使用的锁分段技术,将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。

2、JDK1.8放弃了锁分段的做法,采用CAS和synchronized方式处理并发。以put操作为例,CAS方式确定key的数组下标,synchronized保证链表节点的同步效果。

9、 ConcurrentHashMap的线程安全

HashMap没有考虑并发处理,比如多个线程put的时候,如果key的hash值一样,可能导致数据的覆盖丢失。如果非常巧合,一个线程put,另外一个线程扩容需要移动元素,对资源的引用发送了冲突,就会发送死锁。

?????? 同时还在扩容的时候也会存在问题,比如多个线程,第一个线程正在put,第二个线程也是正在put,第三个线程发现table容量不够用,需要扩容了,此时就会乱套,导致死循环。

?????? 举例:

?????? 第一个线程put, key为a1, value为b1,完成。

?????? 第二个线程put, key为a2, value为b2, next为a1元素,cpu切换;

?????? 第三个线程put,table的size超过阀值(12), 开始扩容两倍,扩容的时候,元素的顺序,根与叶进行了颠倒,这样形成了元素a1的next为a2.

?????? 这时a2元素继续执行,a2设置next为a1,这时就形成了死循环。

可以看下几个方法:transfer、createEntry(hash, key, value,bucketIndex);

那ConcurrentHashMap为什么是线程安全的?

ConcurrentHashMap用到了很多Unsafe的原子性操作方法,内存操作,锁操作,从底层保证数据的原子性、可见性、有序性。

首先ConcurrentHashMap通过key的hash值得到对于的segment

s= ensureSegment(j);

??????? return s.put(key, hash, value, false );

然后在对于的segment进行put操作,这时每个单独的segment的put是上了锁的

?HashEntrynode = tryLock() ? null :

??????????????? scanAndLockForPut(key, hash, value);

?? 这样,在独立的segment段内进行操作,是可以保证线程安全的。

?? ConcurrentHashMap中用到了Unsafe很多方法,比较常见及重要的有:

[if !supportLists]1、 [endif]UNSAFE.getObject,是从指定的内存偏移量地址中获取到对象

[if !supportLists]2、 [endif]UNSAFE.getObjectVolatile, 可以直接获取指定内存的数据,保证了每次拿到数据都是最新的。可见性。

[if !supportLists]3、 [endif]UNSAFE.compareAndSwapObject,比较并更新对象的某一个对象类型的域,1被操作的对象,fieldoffset 被操作的域在对象中的偏移量,expect 域的期望值,update 域的更新值 。

以上主要是针对JDK7进行说明的, JDK8改动太大,不在本文档说明范围内。

i

concurrenthashmap的读是否要加锁,为什么

ConcurrentHashMap为什么支持完全并发的读

ubuntu是Linux的一种,所以Linux的编译器gcc同样也适用于ubuntu。

要在ubuntu下编译gcc,可以按照如下步骤。

1,确认gcc是否安装。

Ubuntu的标准安装,会同步安装gcc编译器,如果没有安装,那么需要手动安装。

在shell下,打gcc --version,如果可以正确显示版本信息,表示已经安装,可以忽略第二步。

2,安装gcc。

在shell下,使用命令

sudo apt-get build-depgcc

即可智能安装gcc,前提为系统需要联网。

3, 执行编译。

执行C语言编译的最简命令为

gcc c_files -o target_name

其中c_files为所有需要编译的C文件列表,target_name为生成的可执行文件名。

执行后,如果有错误,那么需要根据错误进行修改源程序,直至没有错误为止,这时会生成一个与之前设定的target_name同名的可执行文件。通过

./target_name

可以运行该程序。

像ConcurrentHashMap等高性能的类具体的业务使用场景是哪些

需要用类实例变量来存储数据,比如在线人数,登录人数,缓存某类数据。

其实这里也要准确理解并发和并行的概念,广泛的讲,并发是包含并行的,因为并发是指在一段时间内很多线程访问,比如5秒内100条线程访问,那其中是不是有2条同时到达呢?如果同时到达,那就是2条并行线程。

并行和并发产生的安全问题其实是指在类变量或者类实例变量的处理上,比如静态变量或者类初始化一个map变量,这些变量一旦在被调用的方法里出现,这时候只要有多个线程不管是并行或者并发,都可能让变量中的值被改乱。

那么ConcurrentHashMap是应对线程安全的,也就是不管是并行还是并发,类数据都不可能出现线程安全,不会出现值被该乱。

注意:并行常见的场景是事务处理,所谓的分布式事务或数据库事务就是为了处理并行到达的多个线程同时想修改一个全局值。

并发,并不一定是处理同一个全局值,这个概念只是指很多的线程,并不一定是处理同一个业务,比如登录某个系统模块的并发数是1000,那这1000里面可能处理各种不同的业务,这也是并发,那么直到其中有几个线程要共同修改一个值了就是并行,这时候才出现了所谓并行和并发安全问题。

简单总结ConcurrentHashMap

在并发使用到HashMap的时候,往往不建议直接用HashMap,因为HashMap在并发写数据的时候容易因为rehash的过程产生环形链表的情况。所以在并发使用Map结构时,一般建议使用ConcurrentHashMap。

在JDK1.7中ConcurrentHashMap采用了 数组+Segment+分段锁 的方式实现。

从上面的结构我们可以了解到,ConcurrentHashMap定位一个元素的过程需要进行两次Hash操作。第一次Hash定位到Segment,第二次Hash定位到元素所在的链表的头部。

JDK8中ConcurrentHashMap参考了JDK8 HashMap的实现,采用了数组+链表+红黑树的实现方式来设计,内部大量采用CAS操作。并发控制使? synchronized 和 CAS 来操作。 (JDK1.6 以后 对 synchronized 锁做了很多优化) 整个看起来就像是优化过且线程安全的 HashMap,虽然在 JDK1.8 中还能看到 Segment 的数据结构,但

是已经简化了属性,只是为了兼容旧版本;

JDK1.8的Nod节点中value和next都用volatile修饰,保证并发的可见性。

可以理解为,synchronized 只锁定当前链表或红??叉树的?节点,这样只要 hash 不冲突,就不会产?并发,效率?提升 N 倍。

Hashtable 和 JDK1.8 之前的 HashMap 的底层数据结构类似都是采? 数组+链表 的形式,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突?存在的;

Hashtable(同?把锁) :使? synchronized 来保证线程安全,效率?常低下。当?个线程访问同步?法时,其他线程也访问同步?法,可能会进?阻塞或轮询状态,如使? put 添加元素,另?个线程不能使? put 添加元素,也不能使?get,竞争会越来越激烈效率越低;

总结一下:

JavaGuide

hashmap要加锁吗

HashMap 是非线程同步的,如果涉及到多个线程同时操作的情况,需要加锁。

(责任编辑:IT教学网)

更多

相关新书快递文章

推荐新书快递文章