这个代码多线程安全吗?

时间:2013-03-05 21:20:16

标签: java multithreading static-methods

    private static Map<Integer, String> map = null;

    public static String getString(int parameter){

        if(map == null){

            map = new HashMap<Integer, String>();
            //map gets filled here...

        }

        return map.get(parameter);
    }

多线程的代码是不安全的吗?

7 个答案:

答案 0 :(得分:4)

如上所述,这绝对不安全。如果地图的内容不是基于getString()中的参数,那么通过将地图初始化为静态初始化程序可以更好地服务,如下所示:

private static final Map<Integer, String> MAP = new HashMap<Integer,String>();

static {
  // Populate map here
}

当加载类时,上面的代码被调用一次。它完全是线程安全的(虽然未来对地图的修改不是)。 你是否因为性能原因而懒得加载它?如果是这样,那就更安全了:

private static Map<Integer, String> map = null;

public synchronized static String getString(int parameter){

    if(map == null){

        map = new HashMap<Integer, String>();
        //map gets filled here...

    }

    return map.get(parameter);
}

使用synchronized关键字将确保任何时候只有一个线程可以执行该方法,并且始终传播对地图引用的更改。

如果您提出这个问题,我建议您阅读“Java Concurrency in Practice”。

答案 1 :(得分:2)

比赛条件?可能。

如果mapnull,并且两个线程同时检查if (map == null),则每个线程将分配一个单独的映射。这可能是也可能不是问题,主要取决于map是否不变。即使地图不变,填充地图的成本也可能成为一个问题。

内存泄漏?否

无论竞争条件如何,垃圾收集器都能正常工作。

答案 2 :(得分:1)

在多线程方案中,您确实冒了两次初始化map的风险。

在托管语言中,垃圾收集器最终将处理不再引用的实例。在非托管语言中,您永远不会释放为覆盖的地图分配的内存。

无论哪种方式,都应该正确保护初始化,以便多个线程不会同时运行初始化代码。

一个原因:第一个线程可能正在初始化HashMap,而第二个线程很长,看到map不为空,并且愉快地尝试使用部分初始化的数据结构。

答案 3 :(得分:1)

由于竞争条件,在多线程情况下是不安全的。

但是你真的需要对地图进行延迟初始化吗?如果无论如何都要使用地图,似乎你可以为它做急切的初始化..

答案 4 :(得分:1)

上面的代码不像其他人提到的那样是线程安全的,你的地图可以初始化两次。您可能会尝试通过添加一些同步来尝试修复上面的代码,这称为“双重检查锁定”,Here is an article描述了此方法的问题,以及一些可能的修复。

最简单的解决方案是将字段设置为单独类中的静态字段:

class HelperSingleton {
  static Helper singleton = new Helper();
 }

它也可以使用volatile关键字修复,如Bill Pugh的文章所述。

答案 5 :(得分:0)

不,这个代码对多个线程使用是不安全的。

地图初始化中存在竞争条件。例如,多个线程可以同时初始化地图并破坏彼此的写入。

没有内存屏障可确保线程所做的修改对其他线程可见。例如,每个线程都可以使用自己的映射副本,因为它们从不“看到”另一个线程写入的值。

没有原子性可以确保在同时访问映射时保留不变量。例如,执行get()操作的线程可能会进入无限循环,因为另一个线程在同时put()操作期间重新处理了存储桶。

答案 6 :(得分:0)

如果您使用的是Java 6,请使用ConcurrentHashMap

ConcurrentHashMap JavaDoc