精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (6)
|
|
---|---|
作者 | 正文 |
发表时间:2010-08-18
最后修改:2010-08-18
这是我们的天才Lauri Tulmin处理的一个有趣的技术支持的故事。问题看起来是Wicket里的JRebel导致的
private final Map< IModifiable, Entry> modifiableToEntry = new ConcurrentHashMap( ) ; public void start( Duration pollFrequency) { this .task = new Task( "ModificationWatcher" ) ; this .task .run ( pollFrequency, new ICode( ) { public void run( Logger log) { Iterator iter = new ArrayList ( ModificationWatcher.this .modifiableToEntry .values ( ) ).iterator ( ) ; while ( iter.hasNext ( ) ) 很明确,异常是由 让我们先暂停一下,说明一下这段代码为什么要这样写。在使用集合(collections)时有可能会出现一个问题,就是当我们重复迭代这个集合时,如果这个集合不巧被修改了(通常是被另外的线程),程序就会抛出 for ( Iterator i = new ArrayList ( collection) .iterator ( ) ; i.hasNext ( ) ; ) { ...} 为了使collection能在多线程的环境中使用,必须保证它的可同步性和其它相关的特性。 这种用法非常普遍,只要在Google Code 里简单搜一下
就能证明。事实上我们在JRebel程序里多次的这样使用过,在Wicket里的很多地方也是这样用的。所以这怎么会出现 Lauri经过深入的研究发现,这种写法在多线程环境中有天生的缺陷(即使在collection已经被同步锁的情况下!)。原因就在于Java 1.6之前的 public ArrayList ( Collection<? extends E> collection) { int size = collection.size ( ) ; firstIndex = 0 ; array = newElementArray( size + ( size / 10 ) ) ; collection.toArray ( array) ; lastIndex = size; modCount = 1 ; } 问题就出在 那么如何才能避免这个问题?一个办法是在整个循环上加上同步锁,但这就会限制你只能当和其它线程在同一个同步区内才能访问这个集合。有一个简单的解决方案,就是使用像下面这样使用 for ( Iterator i = Arrays .asList ( collection.toArray ( ) ) .iterator ( ) ; i.hasNext ( ) ; )
本文来自:外刊IT评论
:)
声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2010-08-18
抱歉,布局是在是搞不好
|
|
返回顶楼 | |
发表时间:2010-08-19
不知道我观点对不对,请抛砖.
toArray和ArrayList的构造函数一样, 都不是thread safe的. 这是JDK6 ArrayList.toArray()的代码 public Object[] toArray() { Object[] result = new Object[size]; //时间点1 System.arraycopy(elementData, 0, result, 0, size); //时间点2 return result; } 当原始list shrink的时候, T1和T2对size会有自己的缓存里的local copy, T1在时间点1记录的size, T2如果减小了list, T1因此仍然会在时间点2数组越界. 我觉得你的解决方案也不会根治这个问题,只是大大的降低了概率. for(Iterator i = Arrays.asList(collection.toArray()).iterator();i.hasNext();) 理论上这里toArray可能会有问题, 除非你保证所有对collection的更改都是collection被发布之后,也就是说不更改原来的collection, 否则即便在循环上加锁也没有用. |
|
返回顶楼 | |
浏览 1381 次