关于书籍示例的问题 - 实践中的Java并发,清单4.12

时间:2010-04-05 18:53:01

标签: java synchronization immutability

我正在研究Java Concurrency in Practice中的一个例子,我不明白为什么在以下代码中需要一个并发安全的容器。

我没看到容器怎么样     “地点”的状态 施工后可以修改;因为它是通过'unmodifiableMap'包装器发布的,所以在我看来普通的HashMap就足够了。

EG,它是同时访问的,但地图的状态只能由读者访问,不能由作者访问。地图中的值字段通过委托到“SafePoint”类同步,因此虽然这些点是可变的,但是地图中的哈希键及其关联值(对SafePoint实例的引用)永远不会改变。

我认为我的困惑是基于问题的集合状态究竟是什么。

谢谢! -Mike

代码清单4.12,Java Concurrency in Practice, (此列表以.java here的形式提供,也可通过google以章节形式提供)

/////////////开始代码

@ThreadSafe
public class PublishingVehicleTracker {

private final Map<String, SafePoint> locations;
private final Map<String, SafePoint> unmodifiableMap;

public PublishingVehicleTracker(
                        Map<String, SafePoint> locations) {
    this.locations
        = new ConcurrentHashMap<String, SafePoint>(locations);
    this.unmodifiableMap
        = Collections.unmodifiableMap(this.locations);
}

public Map<String, SafePoint> getLocations() {
    return unmodifiableMap;
}

public SafePoint getLocation(String id) {
    return locations.get(id);
}

public void setLocation(String id, int x, int y) {
    if (!locations.containsKey(id))
        throw new IllegalArgumentException(
            "invalid vehicle name: " + id);
    locations.get(id).set(x, y);
  }
}

// monitor protected helper-class

@ThreadSafe
public class SafePoint {

@GuardedBy("this") private int x, y;

private SafePoint(int[] a) { this(a[0], a[1]); }

public SafePoint(SafePoint p) { this(p.get()); }

public SafePoint(int x, int y) {
    this.x = x;
    this.y = y;
}

public synchronized int[] get() {
    return new int[] { x, y };
}

public synchronized void set(int x, int y) {
    this.x = x;
    this.y = y;
}

}

///////////结束代码

3 个答案:

答案 0 :(得分:4)

你是对的。我认为这是JCiP中的一个错误。如果您想要双重确定,我建议您将其发布到(其)邮件列表: http://gee.cs.oswego.edu/dl/concurrency-interest

就像你说的那样,地图没有被修改;修改该值不会导致地图上的任何“写入”。

事实上,我的生产代码完全符合您的建议,我在上述邮件列表中询问了有关该代码的问题。 JCiP的作者之一告诉我,可以为容器使用只读的hashmap。

以下是我的代码的简化版本(无空检查等)(我最终使用google-collection的ImmutableMap。):

class Sample {
    private final ImmutableMap<Long, AtomicReference<Stuff>> container;

    Sample(){
        this.container = getMap();
    }

    void setStuff(Long id, Stuff stuff){
        AtomicReference<Stuff> holder = container.get(id);
        holder.set(stuff);
    }
}

它在极端负荷下完美地工作了很长时间。

答案 1 :(得分:1)

方法setLocation确实修改了地图的内容。

更新:我与同事讨论了这个问题,最后我们同意了@Zwei Steinen的结论(+1给他: - )。

请注意,除了他所提到的,可见性也是一个问题。但是,在这种情况下,地图被声明为final,这可以保证这一点。

答案 2 :(得分:1)

@Zwei,感谢你的建议 - Joe Bowbeer&amp; Tim Peierls(JCiP共同作者)证实,从技术上讲,普通的哈希映射就足够了。 (虽然任何现实世界的设计都可能有额外的要求,需要并发地图)

原因是底层地图是
- 从最终字段可以灵敏地到达
- 自从'getLocations()'发布以来没有改变(实际上它在ctor之后从未改变,但这不是必需的)
- 正确构建

乔向我指了几篇非常好的博客文章,解释了更多关于java不变性的真正含义:

http://jeremymanson.blogspot.com/2008/04/immutability-in-java.html
http://jeremymanson.blogspot.com/2008/07/immutability-in-java-part-2.html

我会向那里的读者推荐一个完整的解释。

谢谢!