【问题标题】:Is this way of passing and modifying a hashmap threadsafe这种解析和修改hashmap线程的方式是否安全
【发布时间】:2013-05-29 19:39:54
【问题描述】:

这是一小段代码

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
}
}

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

【问题讨论】:

  • 所有单线程代码都是线程安全的。
  • @MarkoTopolnik,当然是 :) 我的问题是我发布的上述 sn-p 在多线程环境中是否是线程安全的?
  • @SotiriosDelimanolis 当然。并且对象被传递给不同的方法,在那里它被改变(只是没有声明),也将是线程安全的?
  • 您对“多线程环境”到底有什么想法?其他线程闲逛,做他们的事情?您必须准确指出您所涉及的线程和位置。
  • 关于您的一小段代码,我可以告诉您的一件事是,= new HashMap&lt;String,Ojbect&gt;(); 部分毫无意义。你应该删除它。

标签: java multithreading concurrency thread-safety


【解决方案1】:

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

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

【讨论】:

  • 确定不是。但是当每个线程都有自己的方法本地对象堆栈时,我想知道是否会在一个不是线程安全的方法中声明一个 HashMap。 ConcurrentHashMap 不是在同一个类的不同地方改变哈希图状态的情况下使用吗?
  • A) 如果你的类的一个实例被多个线程引用,你就完蛋了。 B) ConcurrentHashMap 用于在一个线程中修改并在另一个线程中读取或写入时使用。
【解决方案2】:

问题出在这里:

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 在 java.util.HashMap$HashIterator.nextEntry(HashMap.java:793) 在 java.util.HashMap$KeyIterator.next(HashMap.java:828) 在 Test$1.findTruckParts(Test.java:19) at Test$1.run(Test.java:12) 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)

我希望这会有所帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-05
    • 1970-01-01
    • 2012-11-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多