调用IsItThreadsafe.getValue(...)
的线程是否会从变量IsItThreadsafe.map
引用的Map中获取最新值(假设在IsItThreadSafe.main(...)
中实现了确切的使用场景?
public class IsItThreadSafe {
private Map<String, String> map;
public synchronized void setMap(Map<String, String> map) {
this.map = map;
}
public synchronized String getValue(String key) {
if (this.map == null) {
this.map = new HashMap<>();
}
return map.get(key);
}
public static void main(String[] args) throws Exception {
IsItThreadSafe sharedObject = new IsItThreadSafe();
Thread t1 = new Thread(() -> {
while (true) {
for (int i = 0; i < 10; i++) {
String key = String.valueOf(i);
String value = sharedObject.getValue(key);
if (value!=null) {
System.out.println("key = " + key + " value = " + value);
}
}
}
});
t1.start();
Thread t2 = new Thread(() -> {
while (true) {
Map<String, String> mappedData = new HashMap<>();
for (int i = 0; i < 10; i++) {
mappedData.put(String.valueOf(i), String.valueOf(i + 1));
}
sharedObject.setMap(mappedData);
}
});
t2.start();
t1.join();
t2.join();
}
}
答案 0 :(得分:1)
除非setMap
调用者继续修改传递给setMap
的地图,否则它应该正常工作。一旦setMap
被调用,它应该忘记这个地图。这样的版本一般会更安全:
public synchronized void setMap(Map<String, String> map) {
this.map = new HashMap<>(map);
}
当然,虽然您的代码是正确的,但并不意味着代码是最佳的。特别是如果您不打算将新值放入地图中,则每次查询值时都可以摆脱同步。例如,您可以使用AtomicReference
:
private final AtomicReference<Map<String, String>> map = new AtomicReference<>();
public void setMap(Map<String, String> map) {
this.map.set(new HashMap<>(map));
}
public String getValue(String key) {
Map<String, String> m = this.map.get();
// loop is necessary if setMap(null) call occurs
while (m == null) {
// Use CAS to avoid possible overwrite of concurrent setMap() call
m = new HashMap<>();
if(!this.map.compareAndSet(null, m))
m = this.map.get();
}
return m.get(key);
}
这里同步被volatile读取取代,这通常要快得多。