DelegatingVehicleTracker(第65页Goetz)如何返回“实时”视图?

时间:2015-11-04 21:51:44

标签: java multithreading concurrency delegation

在Java Concurrency in Practice的第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));
}
}

关于本课程Goetz写道:

  

“......委托版本[上面的代码]返回一个不可修改但是   车辆位置的“实时”视图。这意味着如果线程A调用   getLocations()和线程B稍后修改了一些的位置   点,这些变化反映在返回到线程A的地图中。“

Thread A的不可修改的Map在什么意义上是“实时的”?我没有看到Thread B通过调用setLocation()所做的更改将如何反映在Thread A的unmodifiableMap中。只有当Thread A构造了DelegatingVehicleTracker的新实例时,情况才会如此。但是如果线程A持有对这个类的引用,我不知道这是如何可能的。

Goetz继续说getLocationsAsStatic()可以被称为“对所需舰队的不变观点”。我很迷惑。在我看来恰恰相反的情况是,对getLocationsAsStatic()的调用确实会返回“实时”视图,而对getLocations()的调用是不重新构造的类,会返回静态的,不变的视图车队。

在这个例子中我缺少什么?

任何想法或观点都表示赞赏!

3 个答案:

答案 0 :(得分:4)

我认为你的困惑是由于对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
}

因此,unmodifiableMap示例中的DelegatingVehicleTracker由可变地图locations(一个thread-safe)支持。 setLocation以原子方式对locations进行变异,因此对于持有对unmodifiableMap的引用的线程,可以看到更改,因为它们知道这些线程不能改变unmodifiableMap。 读者无法访问locations,因此只会通过DelegatingVehicleTracker进行变更,因此名称为delegation

答案 1 :(得分:1)

  

在什么意义上线程A的不可修改的地图将是&#34;直播&#34;?我没有看到线程B通过调用setLocation()所做的更改将如何反映在线程A的不可修改的地图中

这是因为getLocations()返回实际可变地图的不可修改的包装地图。

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

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

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

  

Goetz接着说,可以调用getLocationsAsStatic()是一个不变的视图,需要对舰队进行调查&#34;

此代码

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

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

答案 2 :(得分:1)

getLocations()会返回只读地图,该地图会在 getLocations()被调用后反映更新。

另一方面,

getLocationsAsStatic()会在调用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)