expectedModCount和modCount关系,以及concurrentHashMap不加读锁的原因
来源:互联网 发布:药物合成工艺优化 编辑:程序博客网 时间:2024/06/06 01:13
public class TestArrayListIterator { public static void main(String[] args) { ArrayList<Integer> list = new ArrayList<Integer>(); list.add(10); Iterator<Integer> iterator = list.iterator(); while(iterator.hasNext()){ Integer integer = iterator.next(); if(integer==10) list.remove(integer); //注意这个地方 } }}
Exception in thread "main" java.util.ConcurrentModificationExceptionat java.util.AbstractList$Itr.checkForComodification(Unknown Source)at java.util.AbstractList$Itr.next(Unknown Source)at com.notify.TestArrayListIterator.main(TestArrayListIterator.java:17)
事实上在我们remove掉这个元素之后 ,该ArrayList的size()的值就变为了0,而此时Iterator的游标cursor是 1 ,在ArrayList迭代器的hasNext()方法中
public boolean hasNext() { return cursor != size();}
然后循环又继续跑了!!!
如果我们不进行modCount和expectedModCount(创建迭代器的时候将当时的modCount赋值给expectedModCount),这个程序肯定会报ArrayIndexOutOfBoundsException,这样的异常显然不是应该出现的(这些运行时错误都是使用者的逻辑错误导致的,我们的JDK那么高端,不会出现使用错误,我们只抛出使用者造成的错误,而这个错误是设计者应该考虑的),为了避免出现这样的异常,定义了检查。
又想,为什么不把hasNext()的判断改为cursor <=size()呢?但是我们还有可能 add()这样的话就会导致数据混乱,事实上线程安全本身就不允许读的时候被修改
==================================
ConcurrentHashMap完全允许多个读操作并发进行,读操作并不需要加锁。(事实上,ConcurrentHashMap支持完全并发的读以及一定程度并发的写。)如果使用传统的技术,如HashMap中的实现,如果允许可以在hash链的中间添加或删除元素,读操作不加锁将得到不一致的数据。但是ConcurrentHashMap实现技术是保证HashEntry几乎是不可变的。HashEntry代表每个hash链中的一个节点,其结构如下所示:
可以看到除了value不是final的,其它值都是final的,这意味着不能从hash链的中间或尾部添加或删除节点,因为这需要修改next引用值,所有的节点的修改只能从头部开始。对于put操作,可以一律添加到Hash链的头部。但是对于remove操作,可能需要从中间删除一个节点,这就需要将要删除节点的前面所有节点整个复制一遍,最后一个节点指向要删除结点的下一个结点。为了确保读操作能够看到最新的值,将value设置成volatile,这避免了加锁。 remove操作要注意一个问题:如果某个读操作在删除时已经定位到了旧的链表上,那么此操作仍然将能读到数据,只不过读取到的是旧数据而已,这在多线程里面是没有问题的。
HashEntry 类的 value 域被声明为 Volatile 型,Java 的内存模型可以保证:某个写线程对 value 域的写入马上可以被后续的某个读线程“看”到。在 ConcurrentHashMap 中,不允许用 null作为键和值,当读线程读到某个 HashEntry 的 value 域的值为 null 时,便知道产生了冲突——发生了重排序现象,需要加锁后重新读入这个 value 值。这些特性互相配合,使得读线程即使在不加锁状态下,也能正确访问 ConcurrentHashMap。
在看源码实现时,对HashEntry 的 value 域的值可能为 null有些疑惑,网上都是说发生了重排序现象,后来仔细想想不完全正确,重排序发生在删除操作时,这只是其中的一个原因,尽管ConcurrentHashMap不允许将value为null的值加入,但现在仍然能够读到一个为空的value就意味着此值对当前线程还不可见,主要因为HashEntry还没有完全构造完成导致的,所以对添加和删除(对链表的结构性修改都可能会导致value为null)。
1 0
- expectedModCount和modCount关系,以及concurrentHashMap不加读锁的原因
- 使用ConcurrentHashMap的原因
- HashMap和Hashtable以及ConcurrentHashMap的区别
- modCount的作用
- modCount
- 介绍ConcurrentHashMap以及ConcurrentHashMap的内部实现
- Arraylist中的modCount 的作用
- modCount到底是干什么的呢
- modCount到底是干什么的呢
- modCount到底是干什么的呢
- Arraylist源码中modCount的位置
- HashMap、hashtable以及ConcurrentHashMap的区别
- ConcurrentHashMap的优点以及实现…
- SqlConnection与IDispose的关系和用using的原因
- uri和url以及urn的关系
- cdc 和 hdc 的关系以及转换
- .config 和 kconfig以及 makefile的关系
- 类和接口以及之间的关系
- Java 封装update,Class使用
- 从线上问题谈spring生命周期类lifeCycle类和bean的生命周期
- 漫步微积分二十八——极限思想下的面积计算
- 一个支持 cgi 的简易 http 服务器
- iOS上使用Quartz 2D绘制简单图形
- expectedModCount和modCount关系,以及concurrentHashMap不加读锁的原因
- NoSQL数据库入门概述
- 常见的软件系统缩写
- Fragment的一些理解
- 【u011】乘法难题
- LeetCode 187 Repeated DNA Sequences (Map)
- 王爽《汇编语言》实验16
- 进程间通信之剪切板
- 如何检测mvc性能和sql语句