【问题标题】:Thread safety issues in reference assignment using method call使用方法调用的引用分配中的线程安全问题
【发布时间】:2014-05-22 17:43:23
【问题描述】:

我有以下代码:

class SomeClass {
    private Map<String, String> someMap = null;

    public String getValue(String key) {
        if (someMap == null) {
            someMap = initialize(); // initialize() is some method which returns a proper map.
        }
        return someMap.get(key);
    }
}

假设我不关心someMap 被多次初始化,这里是否还有其他需要关注的线程安全问题。

根据 what-operations-in-java-are-considered-atomic ,引用分配肯定是 atmoic。对someMap 的分配是否保证在对initialize() 的方法调用之后发生(对我来说似乎合乎逻辑)。线程是否有可能看到部分构造的someMap。如果我有一些其他类型的对象而不是地图,这有关系吗?

【问题讨论】:

    标签: java multithreading thread-safety


    【解决方案1】:

    代码不是线程安全的。如果两个线程在同一个对象上调用getValue 方法,则一个线程可能会看到部分创建的someMap

    为了避免这个问题,您必须删除数据竞争。最简单的解决方案是将someMap 声明为volatile。要记住的简单规则是:如果代码不包含数据竞争,那么所有执行都将看起来是顺序一致的。

    【讨论】:

    • 我不明白这个。 someMap 的分配不会发生在initialize 返回之后,作为if 块内的最后一个操作。鉴于引用分配是 atmoic,分配给someMap 的映射如何被部分创建。
    • @Vivek:如果我们只讨论一个线程,那么对someMap 的分配确实发生在initialize 返回之后。但是,如果我们谈论多个线程,那么只有在执行顺序一致时才是正确的。查看Sequential Consistency 上的维基百科文章。
    • 谢谢,现在知道了。如果我没记错的话,使变量 volatile 强制所有本地写入在分配给变量之前刷新到主内存。
    • @Vivek:你只写了一次(或几次)。写操作的开销无关紧要,volatile reads 的开销很低。
    【解决方案2】:

    即使放 volatile 也不是线程安全的,因为如果同时调用两个线程的 getValue 方法,可能会发生以下情况

    1) Thread 1 & Thread 2 检查 if (someMap == null) ,两者都会通过并尝试重新初始化。

    2) 两者都会重新初始化引用。线程 1 返回旧引用,该引用被线程 2 覆盖。

    3) 所以请参考链接Double Checked Locking in Singleton

    【讨论】:

    • 正如我在问题中提到的,我不关心 someMap 被多次初始化。所以你的场景不适用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多