在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()的调用是不重新构造的类,会返回静态的,不变的视图车队。
在这个例子中我缺少什么?
任何想法或观点都表示赞赏!
答案 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)