简体   繁体   English

静态块中的Java非最终静态Map操作

[英]Java non-final static Map Operation in Static Block

I am trying to understand if below code is thread-safe.我试图了解以下代码是否是线程安全的。 I have gone through many many questions in SO but can't seem to find a definite answer.我在 SO 中经历了许多问题,但似乎找不到明确的答案。

class Car {
   public static Map<String, String> features = new HashMap<>();
   static {
       features.put("color", "red");
       features.put("foo", "bar");
   }
   public Comparable<?> getValue(String id) {
       if(!features.containsKey(id)) {
          features.put(id, id);
       }
       String res = features.get(id);
       // some business logic and return stmt.
   }
}

We recently encountered unexpected behaviour in our application, wherein, the value returned by getValue("color") was null.我们最近在应用程序中遇到了意外行为,其中getValue("color")返回的值为 null。 I have been unable to reproduce this issue, but it seems to have happened when two threads were being processed at the same time.我一直无法重现此问题,但似乎是同时处理两个线程时发生的。

  1. The features map is modified only in getValue method if the argument isn't already available in the map.features的地图被修改只在getValue如果参数是不是已经在地图上可用的方法。
  2. The issue occurred for an argument which the map is initialised with, in the static block.问题发生在静态块中映射初始化时使用的参数。
  3. Use case example - Car c = new Car(); c.getValue("color");用例示例 - Car c = new Car(); c.getValue("color"); Car c = new Car(); c.getValue("color");

Any help would be much appreciated.任何帮助将非常感激。 Thanks.谢谢。

No, this isn't thread safe.不,这不是线程安全的。

From the Javadoc of HashMap :来自HashMapJavadoc

Note that this implementation is not synchronized.请注意,此实现不是同步的。 If multiple threads access a hash map concurrently, and at least one of the threads modifies the map structurally, it must be synchronized externally.如果多个线程并发访问一个散列映射,并且至少有一个线程在结构上修改了映射,则必须在外部进行同步。

So, synchronize access to the map:因此,同步对地图的访问:

String res;
synchronized (features) {
  if(!features.containsKey(id)) {
    features.put(id, id);
  }
  res = features.get(id);
}

and make the field final , and synchronize inside the static initializer too.并使该字段为final ,并在静态初始值设定项中同步。

Or, better, use a ConcurrentHashMap , and the computeIfAbsent method.或者,更好的是使用ConcurrentHashMapcomputeIfAbsent方法。

String res = concurrentFeatures.computeIfAbsent(id, k -> id);

( HashMap also has a computeIfAbsent method in Java 8+, but you'd need to invoke that in a synchronized block too). HashMap在 Java 8+ 中也有一个computeIfAbsent方法,但您也需要在同步块中调用它)。


Actually, an even better way to do this in Java 8+ is to use getOrDefault , assuming you don't actually need to keep the previously-unseen key/value pairs:实际上,在 Java 8+ 中执行此操作的更好方法是使用getOrDefault ,假设您实际上不需要保留以前看不见的键/值对:

res = features.getOrDefault(id, id);

This doesn't mutate the map, so you don't have to worry about thread safety here;这不会改变地图,所以你不必担心这里的线程安全; you just have to make sure that it is initialized safely:你只需要确保它被安全地初始化:

public final static Map<String, String> features;
static {
   Map<String, String> f = new HashMap<>();
   f.put("color", "red");
   f.put("foo", "bar");
   features = f;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM