这种传递和修改hashmap线程安全的方式

时间:2013-05-29 19:39:54

标签: java multithreading concurrency thread-safety

这是一段代码

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,输出是带有一些备件信息的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
}
}

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

2 个答案:

答案 0 :(得分:1)

答案是“不”,因为HashMap本身不是线程安全的。 考虑使用线程安全的Map实现,例如ConcurrentHashMap

答案 1 :(得分: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:在线程“Thread-1”中启动异常   java.util.ConcurrentModificationException at   java.util.HashMap $ HashIterator.nextEntry(HashMap.java:793)at at   java.util.HashMap $ KeyIterator.next(HashMap.java:828)at   在Test $ 1.run(Test.java:12)测试$ 1.findTruckParts(Test.java:19)at   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)

我希望这会有所帮助。