DelegatingVehicleTracker (p. 65 Goetz) return 如何查看 "live" 视图?

How does DelegatingVehicleTracker (p. 65 Goetz) return a "live" view?

在 Java 并发实践的第 65 和 66 页,Brian Goetz 列出了以下代码:

@ThreadSafe
public class DelegatingVehicleTracker {
private final ConcurrentMap<String, Point> locations;
private final Map<String, Point> unmodifiableMap;

public DelegatingVehicleTracker(Map<String, Point> points) {
    locations = new ConcurrentHashMap<String, Point>(points);
    unmodifiableMap = Collections.unmodifiableMap(locations);
}

public Map<String, Point> getLocations() {
    return unmodifiableMap;
}

public Point getLocation(String id) {
    return locations.get(id);
}

public void setLocation(String id, int x, int y) {
    if (locations.replace(id, new Point(x, y)) == null)
        throw new IllegalArgumentException("invalid vehicle name: " + id);
}

// Alternate version of getLocations (Listing 4.8)
public Map<String, Point> getLocationsAsStatic() {
    return Collections.unmodifiableMap(
            new HashMap<String, Point>(locations));
}
}

关于此 class Goetz 写道:

"...the delegating version [the code above] returns an unmodifiable but 'live' view of the vehicle locations. This means that if thread A calls getLocations() and thread B later modifies the location of some of the points, those changes are reflected in the map returned to thread A."

在什么意义上线程 A 的 unmodifiableMap 是 "live"?我看不到线程 B 通过调用 setLocation() 所做的更改将如何反映在线程 A 的 unmodifiableMap 中。这似乎只有当线程 A 构造了一个新的 DelegatingVehicleTracker 实例时才会出现这种情况。但是如果线程 A 持有对此 class 的引用,我不明白这怎么可能。

Goetz 继续说 getLocationsAsStatic() 可以被调用 "unchanging view of the fleet required." 我很困惑。在我看来,情况恰恰相反,对 getLocationsAsStatic() 的调用确实 return "live" 视图,而对 getLocations() 的调用确实 class 不是重新构建,将 return 静态、不变的车队视图。

我在这个例子中遗漏了什么?

如有任何想法或观点,我们将不胜感激!

In what sense would Thread A's unmodifiableMap be "live"? I do not see how changes made by Thread B via calls to setLocation() would be reflected in Thread A's unmodifiableMap

这是因为 getLocations() returns 实际可变映射的不可修改包装映射。

public DelegatingVehicleTracker(Map<String, Point> points) {
    locations = new ConcurrentHashMap<String, Point>(points);
    unmodifiableMap = Collections.unmodifiableMap(locations);
}
...

public Map<String, Point> getLocations() {
    return unmodifiableMap;
}

因此,以后的任何更改都将自动反映在原始返回的地图中,因为它们最终都指向同一个内部地图对象。

Goetz goes on to say that getLocationsAsStatic() could be called were an "unchanging view of the fleet required"

这个代码

public Map<String, Point> getLocationsAsStatic() {
return Collections.unmodifiableMap(
        new HashMap<String, Point>(locations));
}

是静态的,因为它 returns 是一个包含所有当前键值对副本的新地图,因此不会反映对 locations 的未来更改。

我认为你的困惑是由于对Collections.unmodifiableMap的误解造成的。 Collections.unmodifiableMap 返回的映射的直接突变是不允许的,但是,突变支持映射完全没问题(只要支持映射允许突变)。例如:

Map<String,String> map = new HashMap<>();
Map<String, String> unmodifiableMap = Collections.unmodifiableMap(map);

map.put("key","value");

for (String key : unmodifiableMap.keySet()) {
   System.out.println(key); // prints key
}

因此,DelegatingVehicleTracker 示例中的 unmodifiableMap 由可变映射 locations(一个 thread-safe 映射)支持。 setLocation 以原子方式改变 locations,因此对于持有对 unmodifiableMap 的引用的线程,知道这些线程不能改变 unmodifiableMap,更改将是可见的。 读者无权访问 locations,因此只能通过 DelegatingVehicleTracker 对其进行修改,因此名称为 delegation.

getLocations() 将 return 一个 只读 映射,它将反映更新 after getLocations() 被调用。

另一方面,

getLocationsAsStatic() 将 return 一个 只读快照(又名深拷贝)的位置图 getLocationsAsStatic() 被调用。

举例说明:

Map<String, Point> locs     = // a map with point A(1,1)  in it
DelegatingVehicleTracker tracker = DelegatingVehicleTracker(locs);

Map<String, Point> snapshot = getLocationsAsStatic();
Map<String, Point> live     = getLocations();

Point newB = // a point A(2,2)
tracker.setLocation(newB);

snapshot.get("A"); // will read A(1,1)
live.get("A");      // will read A(2,2)