【问题标题】:Will JIT optimize this code? Is synchronization required?JIT 会优化这段代码吗?是否需要同步?
【发布时间】:2011-04-14 05:38:52
【问题描述】:

下面是一个包含拼写错误到正确拼写术语的映射的类。该地图由石英作业通过调用 updateCache() 定期更新。方法 updatecache 处理输入映射中的键和值,并将它们存储在临时映射对象中。处理完成后(for 循环后),它将临时映射分配给本地类变量 mispelledToCorrectlySpelled。

package com.test;

导入 java.util.HashMap; 导入 java.util.Map;

导入 org.checkthread.annotations.ThreadSafe;

@ThreadSafe 公共类 SpellCorrectListCacheManager {

private Map<String, String> misspelledToCorrectlySpelled = 
    new HashMap<String, String>(0);

/*
 * invoked by a quartz job thread
 */
public void updateCache(Map<String, String> map) {

    Map<String, String> tempMap = new HashMap<String, String>(map.size());

    for (Map.Entry<String, String> entry : map.entrySet()) {
         //process key and values
        String key = entry.getKey().toLowerCase();
        String value = entry.getValue().toLowerCase();

        tempMap.put(key, value);
    }

    // update local variable
    this.misspelledToCorrectlySpelled = tempMap;
}

/*
 * Could be invoked by *multiple* threads
 */
public Map<String, String> getMisspelledToCorrectlySpelled() {
    return misspelledToCorrectlySpelled;
}

}

问题 1:JIT 优化会优化这段代码吗?

实际代码

/*
     * since tempMap is assigned to misspelledToCorrectlySpelled and not
     * used anywhere else, will JIT remove tempMap as shown in the optimized
     * version below?
     */
    Map<String, String> tempMap = new HashMap<String, String>(map.size());

    for (Map.Entry<String, String> entry : map.entrySet()) {
        // process key and values
        String key = entry.getKey().toLowerCase();
        String value = entry.getValue().toLowerCase();

        tempMap.put(key, value);
    }

    this.misspelledToCorrectlySpelled = tempMap;

优化代码

this.misspelledToCorrectlySpelled = new HashMap<String, String>(map.size());

    for (Map.Entry<String, String> entry : map.entrySet()) {
         //process key and values
        String key = entry.getKey().toLowerCase();
        String value = entry.getValue().toLowerCase();

        this.misspelledToCorrectlySpelled.put(key, value);
    }

问题2:假设JIT不会优化代码,getMisspelledToCorrectlySpelled方法是否应该同步?

/*
     * is this assignment atomic operation?
     * 
     * Does this needs to be synchronized? 
     * 
     * By not synchronizing, the new map may not 
     * be visible to other threads *immediately* -- this is 
     * ok since the new map will be visible after a bit of time 
     * 
     */
    this.misspelledToCorrectlySpelled = tempMap;
}

【问题讨论】:

    标签: java thread-safety jit


    【解决方案1】:

    您应该使用AtomicReference 来存储新地图,以避免同步和可见性问题。但是您的代码中最大的问题是您将非线程安全可变映射的访问权限授予多个线程。您应该将地图包装成不可修改的地图:

    private AtomicReference<Map<String, String>> misspelledToCorrectlySpelled = 
        new AtomicReference<Map<String, String>>(Collections.unmodifiableMap(new HashMap<String, String>(0)));
    
    /*
     * invoked by a quartz job thread
     */
    public void updateCache(Map<String, String> map) {
    
        Map<String, String> tempMap = new HashMap<String, String>(map.size());
    
        for (Map.Entry<String, String> entry : map.entrySet()) {
             //process key and values
            String key = entry.getKey().toLowerCase();
            String value = entry.getValue().toLowerCase();
    
            tempMap.put(key, value);
        }
    
        // update local variable
        this.misspelledToCorrectlySpelled.set(Collections.unmodifiableMap(tempMap));
    }
    
    /*
     * Could be invoked by *multiple* threads
     */
    public Map<String, String> getMisspelledToCorrectlySpelled() {
        return misspelledToCorrectlySpelled.get();
    }
    

    回答您关于 JIT 优化的问题:不,JIT 不会删除临时地图的使用。

    【讨论】:

    • 感谢您的 cmets。您将地图包装成不可修改的地图的建议很棒 - 我添加了它。如果我拥有的代码是多线程安全的(不使用同步时的陈旧/可见性问题除外),我不想使用 AtomicReference 或 Volatile。您是否发现不使用 AtomicReference 而不是过时问题?
    • 请参阅本文ibm.com/developerworks/java/library/j-dcl/index.html中的“清单 8. 清单 7 中的优化代码”@ 如何确定 JIT 不会通过在我的代码中删除 tempMap 来优化代码?有没有办法检查 JIT 是否删除了 tempMap?
    【解决方案2】:

    在需要同步的地方也需要同步。 不能假设有关 JIT 的任何事情,除非一个兼容的实现将与 JLS 和 Java 内存模型兼容,并将遵守使用这些规则设计的代码。 (有多种同步方法,并非都使用synchronized关键字。)

    同步在此处是必需的,除非“可以”看到陈旧的版本。 (这可能不是这里的情况,它可能是一个非常陈旧的版本,带有缓存等等——所以不值得赌注!)。 “引用的分配”本身是原子的,因为不会发生“部分写入”,但不能保证在所有线程中[立即]传播(“可见”)。

    编码愉快。

    【讨论】:

    • 感谢您的 cmets。我可以看到陈旧版本 - 我的假设是更新版本将在最多 10 秒内对所有线程可见。我的假设正确吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-16
    • 1970-01-01
    相关资源
    最近更新 更多