关于Hashtable线程安全的疑问

很多地方都会描述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();
        }
    }