简体   繁体   English

安全发布java.util.concurrent集合

[英]Safe publication of java.util.concurrent collections

Is volatile redundant in this code? 这段代码中的volatile是多余的吗?

public class Test {
    private volatile Map<String, String> map = null;

    public void resetMap() { map = new ConcurrentHashMap<>(); }

    public Map<String, String> getMap() { return map; }
}

In other words, does map = new ConcurrentHashMap<>(); 换句话说, map = new ConcurrentHashMap<>(); provide any visibility guarantees? 提供任何可见性保证?

As far as I can see, the only guarantee provided by ConcurrentMap is: 据我所知, ConcurrentMap提供的唯一保证是:

Actions in a thread prior to placing an object into a ConcurrentMap as a key or value happen-before actions subsequent to the access or removal of that object from the ConcurrentMap in another thread. 在将对象作为键或值放入ConcurrentMap之前的线程中的操作发生在从另一个线程中的ConcurrentMap访问或删除该对象之后的操作之前。

How about other thread safe collections in java.util.concurrent (CopyOnWriteArrayList, etc.)? java.util.concurrent中的其他线程安全集合(CopyOnWriteArrayList等)怎么样?

volatile is not redundant as you are changing the reference to the map. 当您更改对地图的引用时, volatile不是多余的。 ie ConcurrentMap only provides guarentees about the contents of the collection, not references to it. 即ConcurrentMap仅提供有关集合内容的保证,而不是对它的引用。

An alternative would be 另一种选择

public class Test {
    private final Map<String, String> map = new ConcurrentHashMap<>();

    public void resetMap() { map.clear(); }

    public Map<String, String> getMap() { return map; }
}

How about other thread safe collections in java.util.concurrent (CopyOnWriteArrayList, etc.)? java.util.concurrent中的其他线程安全集合(CopyOnWriteArrayList等)怎么样?

Only the behaviour of the collection is thread safe. 只有集合的行为是线程安全的。 Reference to the collection are not thread safe, elements in the collection are not made thread safe by adding them to the collection. 对集合的引用不是线程安全的,通过将集合中的元素添加到集合中,元素中的元素不会成为线程安全的。

volatile is necessary here. 这里需要volatile It applies to the reference, not to what it references to. 它适用于参考,而不适用于它所引用的内容。 In other words it doesn't matter that an object is thread safe, other threads won't see the new value of map field (eg might see previously referenced concurrent map or null ). 换句话说,对象是线程安全的并不重要,其他线程将不会看到map字段的新值(例如,可能会看到先前引用的并发映射或null )。

Moreover, even if your object was immutable (eg String ) you would still need volatile , not to mention other thread-safe collections like CopyOnWriteArrayList . 而且,即使你的对象是不可变的(例如String ),你仍然需要volatile ,更不用说其他线程安全的集合,比如CopyOnWriteArrayList

This is not just about the references. 这不仅仅是参考文献。 Generally, without the volatile modifier other threads may observe a new reference to an object, but observe the object in a partially constructed state. 通常,如果没有volatile修饰符,其他线程可能会观察到对象的新引用,但会以部分构造的状态观察对象。 In general, it is not easy to know, even after consulting the documentation, which objects are safe for publication by a data race. 一般而言,即使在查阅文档后,也不容易知道哪些对象可以安全地通过数据竞争发布。 An interesting note is that the JLS does guarantee this for thread-safe immutable objects , so if the docs mention those two properties it should be enough. 一个有趣的注意事项是JLS确实为线程安全的不可变对象保证了这一点,所以如果文档提到这两个属性就足够了。

ConcurrentHashMap is obviously not an immutable object, so that doesn't apply, and the docs don't mention anything about publication by a data race. ConcurrentHashMap显然不是一个不可变对象,因此不适用,并且文档没有提及有关数据竞争发布的任何内容。 By careful inspection of the source code we may conclude that it is indeed safe, however I wouldn't recommend relying on such findings without this property being clearly documented. 通过仔细检查源代码,我们可以得出结论它确实是安全的,但是如果没有明确记录这些属性,我不建议依赖这些结果。

Memory Consistency Properties 内存一致性属性

A write to a volatile field happens-before every subsequent read of that same field. 在每次后续读取同一字段之前,会发生对易失性字段的写入。 Writes and reads of volatile fields have similar memory consistency effects as entering and exiting monitors, but do not entail mutual exclusion locking. 易失性字段的写入和读取具有与进入和退出监视器类似的内存一致性效果,但不需要互斥锁定。

Actions in a thread prior to placing an object into any concurrent collection happen-before actions subsequent to the access or removal of that element from the collection in another thread. 在将对象放入任何并发集合之前的线程中的操作发生在从另一个线程中的集合访问或移除该元素之后的操作之前。

OK - I was able to construct an example that breaks (on my machine: JDK 1.7.06 / Win 7 64 bits) if the field is not volatile - the program never prints Loop exited if map is not volatile - it does print Loop exited if map is volatile. 好的 - 我能够构建一个断开的示例(在我的机器上:JDK 1.7.06 / Win 7 64位)如果该字段不是易失性的 - 如果map不是volatile,则程序永远不会打印Loop exited - 它确实打印Loop exited如果地图是易变的。 QED. QED。

public class VolatileVisibility extends Thread {

    Map<String, String> stop = null;

    public static void main(String[] args) throws InterruptedException {
        VolatileVisibility t = new VolatileVisibility();
        t.start();
        Thread.sleep(100);
        t.stop = new ConcurrentHashMap<>(); //write of reference
        System.out.println("In main: " + t.stop); // read of reference
        System.out.println("Waiting for run to finish");
        Thread.sleep(200);
        System.out.println("Still waiting");
        t.stop.put("a", "b"); //write to the map
        Thread.sleep(200);
        System.exit(0);
    }

    public void run() {
        System.out.println("In run: " + stop); // read of reference
        while (stop == null) {
        }
        System.out.println("Loop exited");
    }
}

My impression is that Doug Lea's concurrent objects can be safely published by data race, so that they are "thread-safe" even if misused. 我的印象是Doug Lea的并发对象可以通过数据竞争安全地发布,因此即使被滥用也是“线程安全的”。 Though he probably wouldn't advertise that publicly. 虽然他可能不会公开宣传。

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

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