【问题标题】:How does DelegatingVehicleTracker (p. 65 Goetz) return a "live" view?DelegatingVehicleTracker (p. 65 Goetz) 如何返回“实时”视图?
【发布时间】:2016-02-05 14:10:07
【问题描述】:

在 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 的映射中。”

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

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

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

感谢任何想法或观点!

【问题讨论】:

    标签: java multithreading concurrency delegation


    【解决方案1】:

    我认为您的困惑是由于对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 由可变映射 locationsthread-safe 之一)支持。 setLocation 以原子方式改变locations,因此对于持有对unmodifiableMap 的引用的线程来说,更改将是可见的,因为知道这些线程不能改变unmodifiableMap。 读者无权访问locations,因此只能通过DelegatingVehicleTracker 对其进行变异,因此名称为delegation

    【讨论】:

    • 感谢Sleiman,也感谢其他人;现在我懂了。我会对自己说清楚(希望对其他人有用): DelegatingVehicleTracker 可以修改,但客户端类不能。在上面的示例代码中调用 unmodifiableMap 会引发异常(我试过了),但当然,插入到 map 不会。 UnmodifiableMap 就像是透过玻璃屏幕观看的木偶戏,阻止观众干预。
    • @BenWeaver 你刚才描述的比喻有一个术语,它是委托
    【解决方案2】:

    在什么意义上线程 A 的 unmodifiableMap 是“活的”?我看不到线程 B 通过调用 setLocation() 所做的更改将如何反映在线程 A 的 unmodifiableMap 中

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

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

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

    Goetz 继续说 getLocationsAsStatic() 可以被称为“所需舰队的不变视图”

    这段代码

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

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

    【讨论】:

      【解决方案3】:

      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)
      

      【讨论】:

      • 这实际上是问题所在,这是 OP 不理解的内容
      猜你喜欢
      • 2011-07-16
      • 1970-01-01
      • 1970-01-01
      • 2012-06-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多