java多线程处理迭代器_Java并发(五):并发,迭代器和容器

在随后的博文中我会继续分析并发包源码,在这里,得分别谈谈容器类和迭代器及其源码,虽然很突兀,但我认为这对于学习Java并发很重要;

ConcurrentModificationException:

JavaAPI中的解释:当不允许这样的修改时,可以通过检测到对象的并发修改的方法来抛出此异常。一个线程通常不允许修改集合,而另一个线程正在遍历它。 一般来说,在这种情况下,迭代的结果是未定义的。 某些迭代器实现(包括由JRE提供的所有通用集合实现的实现)可能会选择在检测到此行为时抛出此异常。 这样做的迭代器被称为"及时失败"迭代器,当他们发现容器在迭代时被修改时,就会报异常;它称不上时一种处理机制,而是一种预防机制,只能作为并发问题的预警指示器.

迭代器与容器:

Vector这个"古老"的容器类相信大家很熟悉了,他是线程安全的没错,我们利用elements()方法遍历,不会出现线程安全的问题:

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 /**

2 * JDK1.8源码3 */

4

5 /**

6 * Returns an enumeration of the components of this vector. The7 * returned {@codeEnumeration} object will generate all items in8 * this vector. The first item generated is the item at index {@code0},9 * then the item at index {@code1}, and so on.10 *11 *@returnan enumeration of the components of this vector12 *@seeIterator13 */

14 public Enumerationelements() {15 return new Enumeration() {16 int count = 0;17

18 public booleanhasMoreElements() {19 return count

22 publicE nextElement() {23 synchronized (Vector.this) {24 if (count

JDK1.8源码: AbstractCollection (been extends by Vector)

但当我们利用foreach进行迭代时,底层自动调用了iterator(),若我们不锁住容器,可能会报ConcurrentModificationException

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 /**

2 * Returns an iterator over the elements in this list in proper sequence.3 *4 *

The returned iterator is fail-fast.5 *6 *@returnan iterator over the elements in this list in proper sequence7 */

8 public Iteratoriterator() {9 return newItr();10 //JDK1.8写成了一个内部类的形式返回迭代器

11 }12

13 /**

14 * An optimized version of AbstractList.Itr15 */

16 private class Itr implements Iterator{17 int cursor; //index of next element to return

18 int lastRet = -1; //index of last element returned; -1 if no such

19 int expectedModCount =modCount;20

21 Itr() {}22

23 public booleanhasNext() {24 return cursor !=size;25 }26

27 //观察下面的代码可以发现,每次迭代一次都要检查容器长度是否改变

28 @SuppressWarnings("unchecked")29 publicE next() {30 checkForComodification();31 int i =cursor;32 if (i >=size)33 throw newNoSuchElementException();34 Object[] elementData = ArrayList.this.elementData;35 if (i >=elementData.length)36 throw newConcurrentModificationException();37 cursor = i + 1;38 return (E) elementData[lastRet =i];39 }40

41 public voidremove() {42 if (lastRet < 0)43 throw newIllegalStateException();44 checkForComodification();45

46 try{47 ArrayList.this.remove(lastRet);48 cursor =lastRet;49 lastRet = -1;50 expectedModCount =modCount;51 } catch(IndexOutOfBoundsException ex) {52 throw newConcurrentModificationException();53 }54 }55

56 @Override57 @SuppressWarnings("unchecked")58 public void forEachRemaining(Consumer super E>consumer) {59 Objects.requireNonNull(consumer);60 final int size = ArrayList.this.size;61 int i =cursor;62 if (i >=size) {63 return;64 }65 final Object[] elementData = ArrayList.this.elementData;66 if (i >=elementData.length) {67 throw newConcurrentModificationException();68 }69 while (i != size && modCount ==expectedModCount) {70 consumer.accept((E) elementData[i++]);71 }72 //update once at end of iteration to reduce heap write traffic

73 cursor =i;74 lastRet = i - 1;75 checkForComodification();76 }77

78 final voidcheckForComodification() {79 if (modCount !=expectedModCount)80 throw newConcurrentModificationException();81 }82 }

JDK1.8源码: Vector

查看代码时可以发现,iterator()的内部类中提供的next,remove等方法都进行了迭代操作,这样的迭代被称为"隐藏迭代器";

部分容器类的toString()方法会在调用StringBuilder的append()同时迭代容器,例如继承了AbstractSet的HashSet类,而AbstractSet又继承于AbstractCollection:

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

//String conversion

/*** Returns a string representation of this collection. The string

* representation consists of a list of the collection's elements in the

* order they are returned by its iterator, enclosed in square brackets

* ("[]"). Adjacent elements are separated by the characters

* ", " (comma and space). Elements are converted to strings as

* by {@linkString#valueOf(Object)}.

*

*@returna string representation of this collection*/

publicString toString() {

Iterator it =iterator();if (!it.hasNext())return "[]";

StringBuilder sb= newStringBuilder();

sb.append('[');for(;;) {

E e=it.next();

sb.append(e== this ? "(this Collection)": e);if (!it.hasNext())return sb.append(']').toString();

sb.append(',').append(' ');

}

}

JDK1.8源码: AbstractCollection

隐藏的迭代器普遍存在,甚至出现在hashCode,equals,contains,remove等方法中,此处不再赘述;

解决办法:

考虑到并发安全性,我们不得不对容器类单独加锁,但同时我们又不希望加锁,那样太损耗性能了,还存在线程饥饿和死锁的风险,极大降低吞吐量和CPU利用率;

作为有限的替代,我们可以考虑"克隆"容器,并在将其副本封闭起来进行迭代,避免了抛出ConcurrentModificationException,但在克隆过程中仍然要对容器加锁;显然,克隆容器存在系统开销,我们在选择这种方案时必须考虑的因素有:容器大小,每个元素上执行的工作,迭代操作相对于其它操作的调用频率,以及相应时间和系统吞吐率的需求,具体应用详见CopyOnWriteArrayList类.

参考材料:

Java并发编程实战

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值