Java 如果在迭代过程中同步,Collections.synchronizedMap是否是线程安全的?

Java 如果在迭代过程中同步,Collections.synchronizedMap是否是线程安全的?,java,synchronized,Java,Synchronized,我正在尝试修复与ConcurrentModificationException相关的错误,同时迭代集合。synchronizedMap 正如Javadoc所要求的,迭代过程已经在地图上同步 我检查了迭代过程,对映射的大小没有明显的修改(调用trace的方法很长,我将再次检查) 除了迭代过程中的修改之外,还有其他可能导致此异常的可能性吗 由于迭代已经同步,据我所知,其他线程将无法进行修改,如add()或remove(),对吗 我对这些东西真的很陌生。任何帮助都将不胜感激 更新 非常感谢大家的帮助,

我正在尝试修复与
ConcurrentModificationException
相关的错误,同时迭代
集合。synchronizedMap

正如Javadoc所要求的,迭代过程已经在地图上同步

我检查了迭代过程,对映射的大小没有明显的修改(调用trace的方法很长,我将再次检查)

除了迭代过程中的修改之外,还有其他可能导致此异常的可能性吗

由于迭代已经同步,据我所知,其他线程将无法进行修改,如
add()
remove()
,对吗

我对这些东西真的很陌生。任何帮助都将不胜感激

更新

非常感谢大家的帮助,特别是@Marco13的详细解释。我编写了一个小代码来测试和验证这个问题,代码附在这里:

public class TestCME {
    public static void main(String[] args){
        TestMap tm = new TestMap();
        for(int i = 0;i < 50;i++){
            tm.addCity(i,new City(i * 10));
        }
        RunnableA rA = new RunnableA(tm);
        new Thread(rA).start();
        RunnableB rB = new RunnableB(tm);
        new Thread(rB).start();
    }
}
class TestMap{
    Map<Integer,City> cityMap;

    public TestMap(){
        cityMap = Collections.synchronizedMap(new HashMap<Integer,City>());
    }

    public Set<Integer> getAllKeys(){
        return cityMap.keySet();
    }

    public City getCity(int id){
        return cityMap.get(id);
    }

    public void addCity(int id,City city){
        cityMap.put(id,city);
    }

    public void removeCity(int id){
        cityMap.remove(id);
    }
}
class City{
    int area;
    public City(int area){
        this.area = area;
    }
}
class RunnableA implements Runnable{
    TestMap tm;
    public RunnableA(TestMap tm){
        this.tm = tm;
    }
    public void run(){
        System.out.println("Thread A is starting to run......");

        if(tm != null && tm.cityMap != null && tm.cityMap.size() > 0){

            synchronized (tm.cityMap){
                Set<Integer> idSet = tm.getAllKeys();
                Iterator<Integer> itr = idSet.iterator();
                while(itr.hasNext()){
                    System.out.println("Entering while loop.....");
                    Integer id = itr.next();
                    System.out.println(tm.getCity(id).area);
                    try{
                        Thread.sleep(100);
                    }catch(Exception e){
                        e.printStackTrace();
                    }
                }
            }

            /*Set<Integer> idSet = tm.getAllKeys();
            Iterator<Integer> itr = idSet.iterator();
            while(itr.hasNext()){
                System.out.println("Entering while loop.....");
                Integer id = itr.next();
                System.out.println(tm.getCity(id).area);
                try{
                    Thread.sleep(100);
                }catch(Exception e){
                    e.printStackTrace();
                }
            }*/

        }
    }
}
class RunnableB implements Runnable{
    TestMap tm;
    public RunnableB(TestMap tm){
        this.tm = tm;
    }
    public void run(){
        System.out.println("Thread B is starting to run......");
        System.out.println("Trying to add elements to map....");
        tm.addCity(50,new City(500));
        System.out.println("Trying to remove elements from map....");
        tm.removeCity(1);
    }
}
公共类TestCME{
公共静态void main(字符串[]args){
TestMap tm=newtestmap();
对于(int i=0;i<50;i++){
tm.addCity(i,新城(i*10));
}
RunnableA rA=新的RunnableA(tm);
新线程(rA).start();
RunnableB rB=新的RunnableB(tm);
新线程(rB.start();
}
}
类测试图{
城市地图;
公共测试图(){
cityMap=Collections.synchronizedMap(新的HashMap());
}
公共集合getAllKeys(){
返回cityMap.keySet();
}
公共城市getCity(内部id){
返回cityMap.get(id);
}
公共无效地址城市(int id,城市){
城市地图放置(id,城市);
}
公共无效删除(内部id){
城市地图移除(id);
}
}
阶级城市{
内部区域;
公共城市(国际区){
这个面积=面积;
}
}
类RunnableA实现Runnable{
TestMapTM;
公共RunnableA(TestMap tm){
this.tm=tm;
}
公开募捐{
System.out.println(“线程A开始运行……”);
如果(tm!=null&&tm.cityMap!=null&&tm.cityMap.size()>0){
同步(tm.cityMap){
设置idSet=tm.getAllKeys();
迭代器itr=idSet.Iterator();
while(itr.hasNext()){
System.out.println(“输入while循环…”);
整数id=itr.next();
System.out.println(tm.getCity(id.area));
试一试{
睡眠(100);
}捕获(例外e){
e、 printStackTrace();
}
}
}
/*设置idSet=tm.getAllKeys();
迭代器itr=idSet.Iterator();
while(itr.hasNext()){
System.out.println(“输入while循环…”);
整数id=itr.next();
System.out.println(tm.getCity(id.area));
试一试{
睡眠(100);
}捕获(例外e){
e、 printStackTrace();
}
}*/
}
}
}
类RunnableB实现Runnable{
TestMapTM;
公共RunnableB(TestMap tm){
this.tm=tm;
}
公开募捐{
System.out.println(“线程B开始运行……”);
System.out.println(“尝试将元素添加到映射…”);
tm.addCity(50,新城(500));
System.out.println(“试图从映射中删除元素…”);
tm.去除率(1);
}
}

我试图恢复我的错误,所以代码有点长,对此我很抱歉。在线程A中,我在映射上进行迭代,而在线程B中,我试图向映射添加元素并从映射中删除元素。有了映射上的正确同步(如@Marco13所建议的),我不会看到ConcurrentModificationException,如果没有同步或TestMap对象上的同步,则会显示异常。我想我现在明白这个问题了。任何关于这一点的双重确认或建议都是非常受欢迎的。再次非常感谢。

基于已添加的代码(同时出于任何原因再次删除)的一些一般性声明

编辑并查看答案底部的更新

您应该知道您实际上在同步什么。使用创建地图时

Map<Key, Value> synchronizedMap = 
    Collections.synchronizedMap(map);
虽然这两种方法可以由不同的线程同时执行,但它是线程安全的:当第一个线程执行
put
方法时,第二个线程必须等待执行
remove
方法。
put
remove
方法将永远不会同时执行。这就是同步的目的


然而,
ConcurrentModificationException
的含义更广泛。关于特定情况,有点简化:
ConcurrentModificationException
表示映射已被修改,而映射上的迭代正在进行中

因此,必须同步整个迭代,而不仅仅是单个方法:

void executedInFirstThread() 
{
    synchronized (synchronizedMap)
    {
        for (Key key : synchronizedMap.keySet())
        {
            System.out.println(synchronizedMap.get(key));
        }
    }
}
void executedInSecondThread() 
{
    synchronizedMap.put(someKey, someValue);
}

没有<代码>同步< /代码>块,第一个线程可以部分地在地图上迭代,然后在迭代的中间,第二个线程可以在地图上调用<代码> PUT/<代码>(从而执行并发修改)。当第一个线程想要继续迭代时,将抛出一个

ConcurrentModificationException

对于
synchronized
块,这是不可能发生的:当第一个线程进入这个
synchronized
块时,第二个线程必须等待,然后才能调用
put
。(它必须等到第一个线程离开
synchronized
块,然后才能安全地进行修改)

void executedInFirstThread() 
{
    synchronized (synchronizedMap)
    {
        for (Key key : synchronizedMap.keySet())
        {
            System.out.println(synchronizedMap.get(key));
        }
    }
}
void executedInSecondThread() 
{
    synchronizedMap.put(someKey, someValue);
}
class TestMap
{
    Map<Key, Value> synchronizedMap = 
        Collections.synchronizedMap(new HashMap<Key, Value>());

    public Set<Key> getAllKeys()
    {
        return synchronizedMap.keySet();
    }
}
    TestMap testMap = new TestMap();
    synchronized(testMap)
    {
        Set<Key> keys = testMap.getAllKeys();
        for (Key key : keys)
        {
            ...
        }
    }
}
class TestMap
{
    Map<Key, Value> synchronizedMap = 
        Collections.synchronizedMap(new HashMap<Key, Value>());

    ...

    public Object getSynchronizationMonitor()
    {
        return synchronizedMap;
    }
}
TestMap testMap = new TestMap();
synchronized(testMap.getSynchronizationMonitor())
{
    ...
}