Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 将不可修改的视图发布到内部地图_Java_Multithreading - Fatal编程技术网

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

我正在阅读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 {

    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
正如广告中提到的,这并不是天生的坏;这完全取决于您的应用程序的需求和期望。

您的“愚蠢”示例实际上非常清楚,非常感谢。