Java 将不可修改的视图发布到内部地图
我正在阅读B.Goetz Java并发的实践,现在我在读关于授权线程安全的部分。他举了以下例子:Java 将不可修改的视图发布到内部地图,java,multithreading,Java,Multithreading,我正在阅读B.Goetz Java并发的实践,现在我在读关于授权线程安全的部分。他举了以下例子: @Immutable public class Point{ public final int x, y; public Point(int x, int y){ this.x = x; this.y = y; } } @ThreadSafe public class DelegatingVehicleTracker { priv
@Immutable
public class Point{
public final int x, y;
public Point(int x, int y){
this.x = x;
this.y = y;
}
}
@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);
unomdifiableMap = 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 id: " + id);
}
}
@不可变
公共课点{
公共最终int x,y;
公共点(整数x,整数y){
这个.x=x;
这个。y=y;
}
}
@线程安全
公共类授权车辆跟踪程序{
私有最终ConcurrentMap位置;
私有最终地图不可修改地图;
公共授权车辆追踪程序(地图点){
位置=新的ConcurrentHashMap(点);
unomdifiableMap=Collections.unmodifiableMap(位置);
}
公共地图getLocations(){
返回不可修改的映射;
}
公共点getLocation(字符串id){
返回位置。获取(id);
}
公共void集合位置(字符串id,int x,int y){
if(locations.replace(id,新点(x,y))==null)
抛出新的IllegalArgumentException(“无效的车辆id:+id”);
}
}
他说
如果线程A调用getLocations,线程B稍后修改
一些点的位置,这些变化反映在
Map
返回到线程A。正如我们前面提到的,这可以是一个
利益(更多最新数据)或责任(潜在
车队视图不一致),具体取决于您的要求
我不明白它的缺点。为什么车队的观点可能变得不一致。所有对象都是不可变的。并非所有对象都是不可变的:位置
不是不可变的,因此不可修改地图
也不是不可变的
这个问题可能没有你想要的那么棘手。由于位置
是线程安全的,不可修改映射
除了对位置
的(不可变)引用之外没有任何状态,因此不存在奇怪的内存可见性问题
奇怪的是,对于这个类的消费者来说,getLocation
似乎可以“神奇地”更改任何给定线程的值。换句话说,如果线程执行此操作:
Point p1 = tracker.getLocation("vehicle1");
Point p2 = tracker.getLocation("vehicle1");
assert p1.equals(p2);
。。。那么代码的编写者可能会惊讶于它曾经失败过。毕竟,对于同一辆车,我只得到了两次积分,并且没有在他们之间调用setLocation
——那么位置怎么会发生变化呢?当然,答案是另一个线程称为setLocation
,我看到在对getLocation
的两次调用之间发生了变化
上面的例子显然有点愚蠢,但不那么愚蠢的例子并不难想象。例如,假设您的应用程序想要对车队进行快照,并且假设两辆卡车不能同时在同一点。在物理世界中,这是一个合理的假设,但应用程序不能这样做,因为在调用getLocation
之间,一辆卡车可能已经移动到另一辆卡车的位置:
Thread1 (taking a snapshot) Thread2 (updating locations)
setLocation("truckA", 10, 10);
setLocation("truckB", 20, 20);
p1 = getLocation("truckA") // (10, 10)
setLocation("truckA", 5, 10);
setLocation("truckB", 10, 10);
p2 = getLocation("truckB") // (10, 10)
assert !p1.equals(p2); // fails
正如广告中提到的,这并不是天生的坏;这完全取决于您的应用程序的需求和期望。您的“愚蠢”示例实际上非常清楚,非常感谢。