ass*_*ias 14 java multithreading hashmap
如果在创建迭代器之后的任何时候对映射进行结构修改,除了通过迭代器自己的remove方法之外,迭代器将抛出ConcurrentModificationException.
我构建了一个示例代码,根据规范,它应该几乎立即失败并抛出ConcurrentModificationException;
注意:它有时不会因Java 7而失败(比如20次中的1次) - 我猜它与线程调度有关(即2个runnables不是交错的).
我错过了什么吗?为什么使用Java 6运行的版本不会抛出ConcurrentModificationException?
实质上,有2个Runnable任务并行运行(倒计时器用于使它们大约同时启动):
然后主线程检查已添加到阵列的键数.
Java 7典型输出(迭代立即失败):
java.util.ConcurrentModificationException
MAX i = 0
Java 6典型输出(整个迭代通过,数组包含所有添加的键):
MAX i = 99
使用的代码:
public class Test1 {
public static void main(String[] args) throws InterruptedException {
final int SIZE = 100;
final Map<Integer, Integer> map = new HashMap<Integer, Integer>();
map.put(1, 1);
map.put(2, 2);
map.put(3, 3);
final int[] list = new int[SIZE];
final CountDownLatch start = new CountDownLatch(1);
Runnable put = new Runnable() {
@Override
public void run() {
try {
start.await();
for (int i = 4; i < SIZE; i++) {
map.put(i, i);
}
} catch (Exception ex) {
}
}
};
Runnable iterate = new Runnable() {
@Override
public void run() {
try {
start.await();
int i = 0;
for (Map.Entry<Integer, Integer> e : map.entrySet()) {
list[i++] = e.getKey();
Thread.sleep(1);
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
};
ExecutorService e = Executors.newFixedThreadPool(2);
e.submit(put);
e.submit(iterate);
e.shutdown();
start.countDown();
Thread.sleep(100);
for (int i = 0; i < SIZE; i++) {
if (list[i] == 0) {
System.out.println("MAX i = " + i);
break;
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
注意:在x86计算机上使用JDK 7u11和JDK 6u38(64位版本).
如果我们将查看HashMap源代码并在Java 6和Java 7之间进行比较,我们将看到如此有趣的区别:
transient volatile int modCount;在Java6中,只transient int modCount;在Java7中.
我确信这是导致所提到的代码的不同行为的原因:
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
Run Code Online (Sandbox Code Playgroud)
UPD:在我看来,这是一个已知的Java 6/7错误:http : //bugs.sun.com/bugdatabase/view_bug.do?bug_id=6625725,它已在最新的Java7中修复.
UPD-2: @Renjith先生说,他刚刚测试过,并没有发现HashMaps实施的行为有任何差异.但我也测试了它.
我的测试是:
1)我创建了HashMap2类,它绝对是HashMap Java 6的副本.
一件重要的事情是我们需要在这里介绍2个新领域:
transient volatile Set<K> keySet = null;
Run Code Online (Sandbox Code Playgroud)
和
transient volatile Collection<V> values = null;
Run Code Online (Sandbox Code Playgroud)
2)然后我HashMap2在测试这个问题时使用它并在Java 7下运行它
结果:它就像这样的测试Java 6,即没有任何ConcurentModificationException.
这一切都证明了我的猜想.QED
作为旁注,ConcurrentModificationException(尽管名称不幸)并不是为了检测跨多个线程的修改.它仅用于捕获单个线程内的修改.无论使用迭代器还是其他任何东西,都可以保证在多个线程之间修改共享HashMap的效果(没有正确的同步).
简而言之,无论jvm版本如何,您的测试都是假的,只有"运气"才能完成任何不同的操作.例如,由于在跨线程查看时HashMap内部处于不一致状态,此测试可能会抛出NPE或其他一些"不可能"的异常.
| 归档时间: |
|
| 查看次数: |
1329 次 |
| 最近记录: |