Java对同一变量

时间:2016-04-27 22:31:48

标签: java multithreading java-memory-model

通过阅读JLS并查看多个场景后,我无法确定Java内存模型遵循线程内语义的规则

仅出于示例目的考虑此代码:

public class CharIndexer {
    public Map<char, int> charLastIndex;

    public void changeCount(String phrase) {
        Map<char, int> newCharLastIndex = new HashMap<char, int>();

        for (int i = 0; i < s.length(); i++){
            newCharLastIndex.put(s.charAt(i),i);
        }

        charLastIndex = newCharLastIndex;
    }
}

在多线程持有对同一CharIndexer实例的引用的场景中,读取字段charLastIndex,而其中一个调用changeCount方法。

如果对charLastIndex字段的赋值(方法的最后一个赋值)在for块之前完成,那么它是否是一个有效的重新排序?

这使得阅读线程可以看到仍然没有填充的地图。

虽然我同意应该使用volatile关键字明确保证可见性,但是线程内语义是否允许这样的重新排序?

两个命令的单线程执行都会授予相同的结果,但是哪些规则确实可以控制内部线程重新排序,从而避免在此实现中对两个块进行重新排序:

public class CharIndexer {
    public Map<char, int> charLastIndex;

    public void changeCount(String phrase) {
        Map<char, int> newCharLastIndex = new HashMap<char, int>();

        for (int i = 0; i < s.length(); i++){
            newCharLastIndex.put(s.charAt(i),i);
        }

        // just changing values around
        foreach(Map.Entry<char,int> charEntry : newCharLastIndex) {
             charEntry.setValue(charEntry.getValue() * 10);
        }

        charLastIndex = newCharLastIndex;
    }
}

我试图了解JIT分析的进展程度,或者我是否对线程内语义的特定规则集一无所知。

1 个答案:

答案 0 :(得分:1)

如果没有发生边界或有效的同步块,则另一个线程看到的操作顺序可能是乱序的。

解释它的最佳方式是,您的HashMap对象和CharIndexer对象可能位于计算机内存的不同部分。

在多CPU服务器上,内存访问由每个CPU独立缓存,因此当CPU#1运行changeCount()方法时,所有操作都在CPU 1缓存中完成。

最终,该缓存刷新到主RAM,一旦他们从主RAM重新加载他们的缓存,其他CPU就可以看到它。

但是,持有HashMap的缓存部分和持有CharIndexer的缓存部分可能不会同时刷新+重新加载。因此,在看到对引用的CharIndexer的更新之前,CPU#2可能会看到HashMap 的更新。

这就是为什么你需要确保HashMap 的构建发生在<{1}}赋值之前,或者两者都将发生 - 在之前使用charLastIndex值。