简体   繁体   English

将不可修改的视图发布到内部地图

[英]Publishing unmodifiable view to an internal map

I'm reading B. Goetz Java Concurrency In Practice and now I'm at the section about delegating thread safety. 我正在阅读B. Goetz Java Concurrency in Practice,现在我在有关委派线程安全的部分中。 He provided the following example: 他提供了以下示例:

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

}

He said that 他说过

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 . 如果线程A调用getLocations并且线程B以后修改了某些点的位置,则这些更改将反映在返回给线程AMap 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. 如前所述,根据您的要求,这可能是收益(最新数据)或负债(船队的视图可能不一致)。

I didn't understand the drawback. 我不明白缺点。 Why is it possible for the view of the fleet to become inconsistent. 为什么机队的观点可能会变得不一致。 All objects are immutable. 所有对象都是不可变的。

All objects are not immutable: locations is not, and so neither is unmodifiableMap . 所有对象都不是一成不变的: locations是不是,所以也不是unmodifiableMap

The problem is potentially less tricky than you're looking for. 该问题可能比您要查找的要难。 Since locations is thread safe, and unmodifiableMap doesn't have any state other than an (immutable) reference to locations , there are no weird memory visibility issues. 由于locations是线程安全的,并且unmodifiableMap除了对locations的(不可变)引用以外没有其他任何状态,因此不会出现奇怪的内存可见性问题。

The weirdness is that to a consumer of this class, getLocation looks like it can "magically" change values from any given thread. 奇怪的是,对于此类的使用者, getLocation看起来可以“神奇地”更改任何给定线程中的值。 In other words, if a thread does this: 换句话说,如果线程执行此操作:

Point p1 = tracker.getLocation("vehicle1");
Point p2 = tracker.getLocation("vehicle1");
assert p1.equals(p2);

... then the writer of that code may be surprised that it ever fails. ...那么该代码的作者可能会惊讶于它曾经失败过。 After all, I just got the point twice for the same vehicle, and didn't call setLocation between them — so how could the location have changed? 毕竟,我只是两次获得了同一辆车的积分,而没有在它们之间调用setLocation -那么位置如何改变? The answer, of course, is that some other thread called setLocation , and I saw that change happen between the two calls to getLocation . 答案当然是另外一个名为setLocation线程,我看到在两次调用getLocation之间发生了更改。

The example above is obviously a bit silly, but less silly examples aren't hard to imagine. 上面的例子显然有点愚蠢,但是更少的愚蠢的例子并不难想象。 For instance, let's say your application wants to do a snapshot of the fleet, and it assumes that two trucks can't be at the same point at the same time. 例如,假设您的应用程序想要对车队进行快照,并且假设两个卡车不能同时在同一地点。 This is a reasonable assumption in the physical world, but it's not one your application can make, because one truck may have moved into another truck's location between calls to 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

As the blurb mentions, this isn't inherently bad; 正如blurb所提到的,这并不是天生的坏事。 it all depends on your application's needs and expectations. 这完全取决于您的应用程序的需求和期望。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM