怎么可以错过(安全的list集合)java集合类在哪个包,Java安全集合类,java线程安全的集合类,
一、ConcurrentModificationException异常
先看两段代码:
第一段(迭代时同时删除 非倒数第二 的元素):
输出:
第二段(迭代时同时删除 倒数第二 的元素):
输出:
对于以上现象的解释如下(网上很多都是分析源码,让人很难理解,我也是一知半解,在此,我用最简单的语言来解释问题的根因):
第一段代码中,每次进行了next()方法时,源码会先判断modCount(集合的已修改次数)和expectedModCount(集合的预期修改次数)是否一致,初始化都是4。当第一个循环时,没有问题,进入分支执行remove,此时modCount就被加1了,与expectedModCount不同,报出异常。说白了就是不能让你做任何修改,包括add操作也是一样的报错。
第二段代码中,是比较巧的,当在倒数第二个元素执行remove后,它的size-1了,再到下次迭代时,与当前位置已经相同,认为已经遍历结束了,所以跳出迭代了,就没去执行next()方法了,因此没有异常了。
为什么它叫ConcurrentModificationException异常,即并发修改异常?
原因是它是为了防止多线程并发时导致这种不一致的发生(虽然可能是单线程的使用方法不当导致,即如以上代码)。这个也是集合的fast-fail(快速失败)机制。
二、安全集合类
1. 早期线程安全的集合
我们先从早期的线程安全的集合说起,它们是Vector和HashTable。
Vector
Vector和ArrayList类似,是长度可变的数组,与ArrayList不同的是,Vector是线程安全的,它给几乎所有的public方法都加上了synchronized关键字。由于加锁导致性能降低,在不需要并发访问同一对象时,这种强制性的同步机制就显得多余,所以现在Vector已被弃用。
HashTable
HashTable和HashMap类似,不同点是HashTable是线程安全的,它给几乎所有public方法都加上了synchronized关键字,还有一个不同点是HashTable的K,V都不能是null,但HashMap可以,它现在也因为性能原因被弃用了。
2. Collections包装方法
Vector和HashTable被弃用后,它们被ArrayList和HashMap代替,但它们不是线程安全的,所以Collections工具类中提供了相应的包装方法把它们包装成线程安全的集合:
Collections针对每种集合都声明了一个线程安全的包装类,在原集合的基础上添加了锁对象,集合中的每个方法都通过这个锁对象实现同步。
3. java.util.concurrent包中的集合
ConcurrentHashMap
ConcurrentHashMap和HashTable都是线程安全的集合,它们的不同主要是加锁粒度上的不同。HashTable的加锁方法是给每个方法加上synchronized关键字,这样锁住的是整个Table对象。而ConcurrentHashMap是更细粒度的加锁。
在JDK1.8之前,ConcurrentHashMap加的是分段锁,也就是Segment锁,每个Segment含有整个table的一部分,这样不同分段之间的并发操作就互不影响。
JDK1.8对此做了进一步的改进,它取消了Segment字段,直接在table元素上加锁,实现对每一行进行加锁,进一步减小了并发冲突的概率。
CopyOnWriteArrayList 和 CopyOnWriteArraySet
它们是加了写锁的ArrayList和ArraySet,锁住的是整个对象,但读操作可以并发执行。
4. 其它Concurrent安全集合
除此之外还有ConcurrentSkipListMap、ConcurrentSkipListSet、ConcurrentLinkedQueue、ConcurrentLinkedDeque等,至于为什么没有ConcurrentArrayList,原因是无法设计一个通用的而且可以规避ArrayList的并发瓶颈的线程安全的集合类,只能锁住整个list,这用Collections里的包装类就能办到。
三、解决ConcurrentModificationException异常
现在重新回头来看第一部分异常的代码,此时我们就可以使用CopyOnWriteArrayList安全集合来解决了:
输出:
值得一提的是,使用Collections封装的安全类还是不能解决此问题:
输出:
原文链接:
如有错误,请更正指出,谢谢!
本文系作者 @河马 原创发布在河马博客站点。未经许可,禁止转载。
暂无评论数据