java - 将不可修改的 View 发布到内部 map

标签 java multithreading

我正在阅读 B. Goetz Java 并发实践,现在正在阅读有关委托(delegate)线程安全的部分。他提供了以下示例:

@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);
    }

}

他这么说

if thread A calls getLocations and thread B later modifies the locations of some of the point, those changes are reflected in the Map returned to thread A. As we remarked earlier, this can be a benefit (more up-to-date data) or a liability (potentially inconsistent view of the fleet), depending on your requirement.

我不明白这个缺点。为什么舰队的视野会变得不一致。所有对象都是不可变的。

最佳答案

所有对象并非都是不可变的:locations不是,所以 unmodifiableMap 也不是.

问题可能没有您想象的那么棘手。自 locations是线程安全的,并且 unmodifiableMap除了对 locations 的(不可变)引用之外,没有任何状态,不存在奇怪的内存可见性问题。

奇怪的是,对于此类消费者来说,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

正如简介中提到的,这本质上并不是坏事;这完全取决于您的应用程序的需求和期望。

关于java - 将不可修改的 View 发布到内部 map ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34585651/

相关文章:

Java 打开外部脚本并提供输入/获取输出

c++ - 如何在 MPI + openmp 中启动多线程?

multithreading - 在基于 SQLite 的 Delphi 应用程序中是否需要使用线程?

java - 逐行以相反顺序读取文件,无需太多开销

java - 内存分配 - Java 与 C++

java - Amazon AWS 通过 Java API 创建 EBS(弹性 block 存储)

java - Win7运行多线程Java项目偶发问题

c - 生产者消费者同步使用两个线程提供不正常的串行输出

multithreading - 用户级线程和内核支持的线程之间的区别?

java - 如何使用Java从.html.gz网页中提取内容?