【发布时间】:2017-12-24 02:49:34
【问题描述】:
当我阅读 Brian Goetz 的 Java Concurrency in Practice 时,我记得他在关于可见性的章节中说过“另一方面,即使不使用同步来发布对象引用,也可以安全地访问不可变对象”。
我认为这意味着如果您发布不可变对象,则所有字段(包括可变最终引用)对可能使用它们的其他线程都是可见的,并且至少在该对象完成构造时是最新的。
现在,我在https://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html 中读到了 “现在,说了这么多,如果在一个线程构造了一个不可变对象(即一个只包含最终字段的对象)之后,你想确保它被所有其他线程正确地看到,你仍然通常需要使用同步。没有其他方法可以确保,例如,对不可变对象的引用将被第二个线程看到。程序从 final 字段中获得的保证应该通过对如何在您的代码中管理并发。”
它们似乎相互矛盾,我不确定该相信哪个。
我还读到如果所有字段都是最终的,那么即使对象不是不可变的,我们也可以确保安全发布。 比如我一直认为Brian Goetz的并发在实践中的这段代码在发布这个类的对象时是没问题的。
@ThreadSafe
public class MonitorVehicleTracker {
@GuardedBy("this")
private final Map<String, MutablePoint> locations;
public MonitorVehicleTracker(
Map<String, MutablePoint> locations) {
this.locations = deepCopy(locations);
}
public synchronized Map<String, MutablePoint> getLocations() {
return deepCopy(locations);
}
public synchronized MutablePoint getLocation(String id) {
MutablePoint loc = locations.get(id);
return loc == null ? null : new MutablePoint(loc);
}
public synchronized void setLocation(String id, int x, int y) {
MutablePoint loc = locations.get(id);
if (loc == null)
throw new IllegalArgumentException("No such ID: " + id);
loc.x = x;
loc.y = y;
}
private static Map<String, MutablePoint> deepCopy(
Map<String, MutablePoint> m) {
Map<String, MutablePoint> result =
new HashMap<String, MutablePoint>();
for (String id : m.keySet())
result.put(id, new MutablePoint(m.get(id)));
return Collections.unmodifiableMap(result);
}
}
public class MutablePoint { /* Listing 4.5 */ }
例如,在此代码示例中,如果最终保证为假,并且线程创建了此类的实例,然后对该对象的引用不为空,但字段位置在另一个线程使用时为空,该怎么办?那个班?
再一次,我不知道哪个是正确的,或者我是否碰巧误解了文章或 Goetz
【问题讨论】:
标签: multithreading concurrency thread-safety visibility immutability