0

这是一小段代码

public Map<String,Object> findTruckParts(Map<String,Object> output){
Map<String,Object>findPartsMap = null;
NewFooInstance newFooInstance = new NewFooInstance();
findPartsMap = PartBuilder.buildPartsOutputMap(output, outputMap);
newFooInstance.buildItem(findPartsMap);
return findPartsMap;
}

outputMap 是一个新的 hashMap,而 output 是一个带有一些备件信息的 hashmap。buildItem 调用了一些在 findPartsMap 周围传递的其他私有方法。

public class NewFooInstance{
buildItem(Map<String,Object> partsMap){
checkPartsValidity(partsMap, fetchMapOfValidParts());
}

checkPartsValidity(Map<String,Object> partsMap,Map<String,Object> partsMap){
//partsMap = update partsMap with missing items fetched from list of valid parts
}
}

上面的线程安全吗?由于所有映射都是各自方法的本地映射,因此我的假设是这是线程安全的。
编辑:我稍微修改了方法。接收一张地图并返回另一张。所以,我的问题是,返回的这个映射是线程安全的吗?它是该方法的本地,因此我认为这将是线程安全的(如果此映射丢失其监视器,则没有其他线程进入将能够更改其值),但是,因为此映射正在其他类和其他方法中进行修改,此映射的这种方法局部性是否跨越不同的类/方法并确保线程安全?

4

2 回答 2

1

答案是“不”,因为HashMap它本身不是线程安全的。

考虑使用线程安全的 Map 实现,例如ConcurrentHashMap

于 2013-05-30T07:16:03.693 回答
1

问题在这里:

public Map<String,Object> findTruckParts(Map<String,Object> output)

尽管相对于结果映射在方法和子方法中看起来是线程安全的,但源映射(即“输出”)仍然存在线程安全问题。当您从中提取数据以放入新的结果映射时,如果它同时被另一个线程更改,您将获得一个ConcurrentModificationException.

下面是一些代码来说明这个问题:

import java.util.HashMap;
import java.util.Map;


public class Test {
  public static void main(String[] args) throws Exception {
    final Map<String, Object> test = new HashMap<String, Object>();

    new Thread(new Runnable() {
      public void run() {
        System.out.println("Thread 1: started");
        findTruckParts(test);
        System.out.println("Thread 1: done");
      }

      public Map<String,Object> findTruckParts(Map<String,Object> output) {
        Map<String, Object> result = new HashMap<String, Object>();
        for(int i=0; i<100000000; i++) {
          for(String key : output.keySet()) {
            result.put("x", output.get(key));
          }
        }

        return result;
      }
    }).start();

    new Thread(new Runnable() {
      public void run() {
        System.out.println("Thread 2: started");

        for(int i=0; i<100000; i++) {
          test.put("y", "y"+i);
          test.remove("y");
        }

        System.out.println("Thread 2: done");
      }
    }).start();
  }

}

输出总是:

线程 1:已启动线程 2:在 java.util.HashMap$HashIterator.nextEntry(HashMap.java:793) 在 java.util.HashMap$KeyIterator.next(HashMap) 的线程“Thread-1”中启动异常 java.util.ConcurrentModificationException .java:828) 在 Test$1.findTruckParts(Test.java:19) 在 Test$1.run(Test.java:12) 在 java.lang.Thread.run(Thread.java:680) 线程 2:完成

因此,即使该findTruckParts()方法创建了自己的映射来返回,如果它必须查看源映射并且其他一些线程正在修改它的键/值,就会出现问题。其他线程只是阅读,它不应该爆炸。但是我不确定您是否想在这种情况下谈论线程安全,因为它仍然不稳定。

帮助线程安全的一种方法是更改main​​方法的第一行:

final ConcurrentHashMap<String, Object> test = new ConcurrentHashMap<String, Object>(new HashMap<String, Object>());

但是您可以看到安全要求是如何被推送给调用者的,这不是很好。因此,为了帮助实现这一点,您还可以更改方法的签名:

public Map<String,Object> findTruckParts(ConcurrentHashMap<String,Object> output);

现在有了线程安全。

因此,正如我在第一行所述,问题出在:

问题在这里:

public Map<String,Object> findTruckParts(Map<String,Object> output)

我希望这有帮助。

于 2013-05-30T13:19:27.803 回答