rjc*_*arr 11 java collections concurrency
我昨天了解到,多年来我一直错误地使用具有并发性的集合.
每当我创建一个需要由多个线程访问的集合时,我将其包装在其中一个Collections.synchronized*方法中.然后,每当改变集合时,我也将它包装在一个synchronized块中(我不知道为什么我这样做,我一定以为我在某处读过它).
但是,在更仔细地阅读API之后,似乎在迭代集合时需要synchronized块.从API文档(用于Map):
当迭代任何集合视图时,用户必须手动同步返回的地图:
这是一个小例子:
List<O> list = Collections.synchronizedList(new ArrayList<O>());
...
synchronized(list) {
for(O o: list) { ... }
}
Run Code Online (Sandbox Code Playgroud)
所以,鉴于此,我有两个问题:
为什么这甚至是必要的?我能想到的唯一解释是他们使用的是默认迭代器而不是托管线程安全迭代器,但他们可以创建一个线程安全的迭代器并修复这个混乱,对吧?
更重要的是,这是完成了什么?通过将迭代放在同步块中,您可以防止多个线程同时进行迭代.但是另一个线程可以在迭代时改变列表,那么synchronized块如何帮助那里呢?不会在其他地方改变列表,不管它是否同步?我错过了什么?
谢谢您的帮助!
为什么这甚至是必要的?我能想到的唯一解释是他们使用的是默认迭代器而不是托管线程安全迭代器,但他们可以创建一个线程安全的迭代器并修复这个混乱,对吧?
迭代一次只能处理一个元素.为了Iterator保证线程安全,他们需要复制该集合.如果做不到这一点,对底层的任何更改Collection都会影响您如何使用不可预测或未定义的结果进行迭代.
更重要的是,这是完成了什么?通过将迭代放在同步块中,您可以防止多个线程同时进行迭代.但是另一个线程可以在迭代时改变列表,那么synchronized块如何帮助那里呢?不会在其他地方改变列表,不管它是否同步?我错过了什么?
synchronizedList(List)通过在实例上同步返回的对象的方法.因此List当你在一个synchronized块内时,没有其他线程可以添加/删除List.
返回的对象的所有方法Collections.synchronizedList()都同步到列表对象本身。每当从一个线程调用一个方法时,调用它的任何方法的每个其他线程都会被阻塞,直到第一个调用完成。
到现在为止还挺好。
但是,这并不从当你修改集合停止另一个线程调用之间以next()它Iterator。如果发生这种情况,您的代码将失败并显示ConcurrentModificationException. 但是如果你也在一个synchronized块中进行迭代,并且你在同一个对象(即列表)上进行同步,这将阻止其他线程调用列表上的任何 mutator 方法,他们必须等到你的迭代线程释放监视器列表对象。关键是 mutator 方法synchronized与迭代器块的对象相同,这就是阻止它们的原因。
请注意,虽然上述保证了基本完整性,但它并不能保证始终正确的行为。您的代码的其他部分可能会做出在多线程环境中不成立的假设:
List<Object> list = Collections.synchronizedList( ... );
...
if (!list.contains( "foo" )) {
// there's nothing stopping another thread from adding "foo" here itself, resulting in two copies existing in the list
list.add( "foo" );
}
...
synchronized( list ) { //this block guarantees that "foo" will only be added once
if (!list.contains( "foo" )) {
list.add( "foo" );
}
}
Run Code Online (Sandbox Code Playgroud)
至于关于线程安全迭代器的问题,确实有一个列表实现,它被称为CopyOnWriteArrayList. 它非常有用,但正如 API 文档中所指出的,它仅限于少数用例,特别是当您的列表很少修改但迭代如此频繁(并且通过如此多的线程)以致同步迭代会导致严重的瓶颈。如果使用不当,它会大大降低应用程序的性能,因为对列表的每次修改都会创建一个全新的副本。
| 归档时间: |
|
| 查看次数: |
867 次 |
| 最近记录: |