下面是一个类,其中包含拼写错误的拼写错误地图。通过调用updateCache(),石英作业定期更新映射。方法updatecache处理输入映射中的键和值,并将它们存储在临时映射对象中。处理完成后(for循环后),它将临时映射分配给本地类变量misspelledToCorrectlySpelled。
package com.test;
import java.util.HashMap; import java.util.Map;
import 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;
}
答案 0 :(得分:3)
您应该使用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不会删除临时地图使用。
答案 1 :(得分:3)
需要同步时需要进行同步。除了兼容的实现符合JLS和Java内存模型并且遵守使用这些规则设计的代码之外,没有任何关于JIT的内容。 (有多种同步方法,并非所有方法都使用synchronized
关键字。)
此处需要同步 ,除非看到陈旧版本“没问题”。 (这可能不是这种情况,它可能是一个带有缓存的所有非常陈旧的版本 - 所以不要打赌!)。 “引用的赋值”本身是原子的,因为不会发生“部分写入”,但不能保证在所有线程中[立即]传播(“可见”)。
快乐的编码。