这是在静态方法中使单例映射线程安全的正确且有效的方法

时间:2012-05-21 17:48:07

标签: java map thread-safety

我想在初始化map时只创建一次锁。这是我正在使用的代码。

public static Map<String, String> getOrderStatusInstance() {
    if (orderStatusMap == null) {
        synchronized (Utility.class) {
            orderStatusMap = new HashMap<String, String>();

            orderStatusMap.put("Key1", "Value1");
            orderStatusMap.put("Key2", "Value2");
            orderStatusMap.put("Key3", "Value3");
            orderStatusMap.put("Key4", "Value4");
        }
    }

    return orderStatusMap;
}

4 个答案:

答案 0 :(得分:9)

不,这是一个坏主意 - 你正在返回一个可变的,非线程安全的地图。您还尝试来实现双重检查锁定,但没有正确执行 - 并且使用静态初始化比使用它更难。

我会创建一个不可变的映射(优先使用Guava),理想情况下作为类初始化的一部分:

private static final ImmutableMap<String, String> STATUS_MAP = ImmutableMap.of(
    "Key1", "Value1",
    "Key2", "Value2",
    "Key3", "Value3",
    "Key4", "Value4");

public static ImmutableMap<String, String> getOrderStatusInstance() {
    return STATUS_MAP;
}

(对于超过5个键/值对,请使用ImmutableMap.Builder。)

你真的需要它比那更懒惰吗?

答案 1 :(得分:2)

几乎正确...想想一个线程检查orderStatusMap == null并获得true的情况。然后调度程序切换到执行相同检查的另一个线程。两个线程都不会执行同步块。

通过在synchronized块中另一次检查null来防止这种情况:

if (orderStatusMap == null) {
    synchronized (Utility.class) {
        if (orderStatusMap == null) {
            Map<String, String> tmp = new HashMap<String, String>();

            tmp.put("Key1", "Value1");
            tmp.put("Key2", "Value2");
            tmp.put("Key3", "Value3");
            tmp.put("Key4", "Value4");

            orderStatusMap = tmp;
        }
    }
}
return orderStatusMap;

是的,这样做两次都没问题。外部检查仍然有助于提高性能,因为在创建地图后,不再需要进行同步块的昂贵步骤。

请记住,这是一种创建hashmap的线程安全方法。它不会使地图成为线程安全的。

P.S。:如果你喜欢这个问题,你可能也喜欢:How to directly initialize a HashMap (in a literal way)?

答案 2 :(得分:0)

您应该对null区块内的synchronized进行另一次检查。

当两个线程调用您的方法时,它们都会发现orderStatusMap为空,其中一个将进入synchronized块,而另一个将阻塞。但最终它将在同步块内传递并再次初始化地图。

答案 3 :(得分:0)

不,这不正确

此处还有不需要来进行延迟初始化(我相信它总是过度使用)所以只需这样做:

private static final Map<String, String> orderStatusMap;
static{
    orderStatusMap = Collections.synchronizedMap(new HashMap<String, String>());//making it thread safe
    //or use a ConcurrentHashMap

    orderStatusMap.put("Key1", "Value1");
    orderStatusMap.put("Key2", "Value2");
    orderStatusMap.put("Key3", "Value3");
    orderStatusMap.put("Key4", "Value4");
}

public static Map<String, String> getOrderStatusInstance() {
    return orderStatusMap;
}