很多地方都会描述java的Hashtable是线程安全的,为什么在两个线程中操作同一个Hashtable,如果不加同步代码,总报异常?代码如下:
package com.HanyouChina.ThreadTest;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
class MusicThread extends java.lang.Thread{
public Hashtable<String, Integer> objtrdHashtable = null;
@Override
public void run() {
Iterator<Map.Entry<String, Integer>> it = objtrdHashtable.entrySet().iterator();
while (it.hasNext()) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
Map.Entry<String,Integer> entry=(Map.Entry<String,Integer>)it.next();
System.out.println(entry.getKey()+"--"+entry.getValue());
if(entry.getValue()%2 == 0)
it.remove();
}
}
}
class GameThread extends java.lang.Thread{
public Hashtable<String, Integer> objtrdHashtable = null;
@Override
public void run() {
for(Map.Entry<String, Integer> item : objtrdHashtable.entrySet())
{
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(item);
}
}
}
public class ThreadApp {
public static Hashtable<String, Integer> objHashtable = new Hashtable<>();
public static void main(String[] args) {
for(int i=0;i<50;i++)
objHashtable.put(String.valueOf(i), i);
MusicThread thdMusic = new MusicThread();
thdMusic.objtrdHashtable = objHashtable;
thdMusic.start();
GameThread thdGame = new GameThread();
thdGame.objtrdHashtable = objHashtable;
thdGame.start();
}
}
异常截图
for(Map.Entry<String, Integer> item : objtrdHashtable.entrySet())
这里会调用迭代器
在迭代器里如果修改Map集合,会改变它的状态,导致报错。
不关多线程的事。
即便你单线程
for循环里如果有put和remove,也会报错。
HashTable 和 HashMap 的 Map.Entry 遍历操作都有这个限制,不允许一边遍历集合,一边删除或添加集合元素,这些操作会改变集合的长度,它的实现校验了一个叫做 modCount 的遍历,它记录了集合被修改的次数,这个解释比较详细,可以参考:modCount 的解释,以下是官方解释:
/**
* The number of times this HashMap has been structurally modified
* Structural modifications are those that change the number of mappings in
* the HashMap or otherwise modify its internal structure (e.g.,
* rehash). This field is used to make iterators on Collection-views of
* the HashMap fail-fast. (See ConcurrentModificationException).
*/
如果真的需要一边遍历、一边添加或者删除元素的话,应该用它的 iterator 方法或者先拷贝一份,通过拷贝集合来遍历,对集合进行相关的操作。
for (Iterator<Map<String, String>> it = data.iterator(); it.hasNext();) {
Map<String, String> map = it.next();
it.remove();
}
iterator 方法会不停迭代到下一个,不会有该限制。
在for循环或者forEach遍历的时候不能对元素进行移除,移除就会遇到这个,如果想移除可以使用
Iterator<Integer> it = list.iterator();
while(it.hasNext()) {
Integer id = it.next();
if (id == 2) {
it.remove();
}
}