0

我在一个线程中按顺序执行以下操作:

int size = hashTable.size();

foreach.... in ... hasTable.values()

做点什么

我的问题是 foreach 会执行 size 次吗?(即使另一个线程同时放置/删除一个元素?

4

4 回答 4

6

不,HashTable在方法级别上是线程安全的(多个线程可以随时调用任何方法)但没有跨方法同步。在您的两条指令之间,其他线程可能会添加/删除甚至清除哈希表。

如果您需要保持这种不变性,请制作一个防御性副本(不必是线程安全的)并size()在该副本上执行 /loop :

Map<K, V> map = null;
synchronized(hashTable) {
  map = new java.util.HashMap<>(hashTable);
}
map.size();
for(V v: map.values()) {
  //...
}

这里 for-each 是安全的,并且可以保证运行 size-times。同样如评论中所述,您可以同步hashTable

synchronized(hashTable) {
  int size = hashTable.size();
  for(V v: hashTable.values()) {
    //...
  }
}

然而,这种解决方案意味着一次只有一个线程可以执行循环(如果您的循环需要一些时间才能完成,这可能会成为瓶颈)。使用防御性副本,每个线程都有自己的副本,并且多个线程可以同时循环。另一方面,如果该解决方案非常大(复制成本高)但迭代非常快,则该解决方案更好。hashTable

于 2012-08-29T17:24:17.727 回答
1

hashTable如果在执行 foreach 期间另一个线程将修改 ,那么您的代码将抛出ConcurrentModificationException.

于 2012-08-29T17:23:00.053 回答
1

Java HashTable size() 后跟 values() 在线程中安全吗?

所以不是values()方法调用是不安全的。这是您打开一个values()HashTable. 如果在迭代时对表进行修改,则迭代器会引发异常。

我的问题是 foreach 会执行 size 次吗?(即使另一个线程同时放置/删除一个元素?

如前所述,HashTable如果在您遍历它时对其进行了修改,它将引发异常。

HashTable几乎已被弃用。如果您正在寻找一个现代的并发版本,HashMap那么您应该使用ConcurrentHashMap在 Java 5 中添加的 版本。ConcurrentHashMap正确处理当您在没有额外同步或复制的情况下迭代它时在后台发生修改的情况:

ConcurrentHashMap<...> map = new ConcurrentHashMap<...>();
int size = map.size();
for (... value : map.values()) {
    // may be called more or less than size if other threads add or delete
    ...
}
于 2012-08-29T17:44:35.370 回答
1

您的示例不是线程安全的,但可以像这样轻松地使其成为线程安全的:

synchronized(hashTable) {
  int size = hashTable.size();
  foreach.... in ... hasTable.values() {
    // do something
  }
}
于 2012-08-29T18:07:14.447 回答