安全发布java.util.concurrent集合

安全发布java.util.concurrent集合,java,concurrency,thread-safety,Java,Concurrency,Thread Safety,此代码中的volatile是冗余的吗 public class Test { private volatile Map<String, String> map = null; public void resetMap() { map = new ConcurrentHashMap<>(); } public Map<String, String> getMap() { return map; } } 公共类测试{ 私有volati

此代码中的volatile是冗余的吗

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

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

    public Map<String, String> getMap() { return map; }
}
公共类测试{
私有volatile Map=null;
public void resetMap(){map=new ConcurrentHashMap();}
公共映射getMap(){return Map;}
}
换句话说,does
map=newconcurrenthashmap()是否提供任何可见性保证

据我所知,唯一的保证是:

在将对象作为键或值放入ConcurrentMap之前,线程中的操作发生在另一个线程中从ConcurrentMap访问或删除该对象之后的操作之前


java.util.concurrent(CopyOnWriteArrayList等)中的其他线程安全集合如何?

volatile
在更改对映射的引用时不是多余的。i、 e.ConcurrentMap只提供有关集合内容的担保,而不提供对集合的引用

另一种选择是

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

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

    public Map<String, String> getMap() { return map; }
}
公共类测试{
私有最终映射=新的ConcurrentHashMap();
public void resetMap(){map.clear();}
公共映射getMap(){return Map;}
}
java.util.concurrent(CopyOnWriteArrayList等)中的其他线程安全集合如何


只有集合的行为是线程安全的。对集合的引用不是线程安全的,将集合中的元素添加到集合中不会使其成为线程安全的。

此处需要volatile
。它适用于引用,而不是它引用的内容。换句话说,对象是线程安全的并不重要,其他线程不会看到
map
字段的新值(例如,可能会看到以前引用的并发映射或
null


此外,即使您的对象是不可变的(例如,
String
),您仍然需要
volatile
,更不用说其他线程安全的集合,如
CopyOnWriteArrayList

,这不仅仅是关于引用。通常,如果没有
volatile
修饰符,其他线程可能会观察到对对象的新引用,但会在部分构造状态下观察对象。一般来说,即使查阅了文档,也不容易知道哪些对象可以通过数据竞赛安全发布。一个有趣的注意事项是,对于线程安全的不可变对象,确实保证了这一点,因此如果文档提到这两个属性,就足够了

ConcurrentHashMap
显然不是一个不变的对象,因此这不适用,文档中也没有提到任何关于数据竞争发布的内容。通过仔细检查源代码,我们可能会得出结论,它确实是安全的,但是我不建议在没有明确记录此属性的情况下依赖这些发现。

对易失性字段的写入发生在对该字段的每次后续读取之前。易失性字段的写入和读取与进入和退出监视器具有类似的内存一致性效果,但不需要互斥锁定

在将对象放入任何并发集合之前,线程中的操作发生在另一个线程的集合中访问或移除该元素之后的操作之前


好的-我能够构造一个示例,如果字段不易失性,则中断(在我的机器上:JDK 1.7.06/Win 7 64位)-如果map不易失性,程序从不打印
循环退出
,如果map不易失性,则打印
循环退出
。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");
    }
}
公共类volatileviability扩展线程{
Map stop=null;
公共静态void main(字符串[]args)引发InterruptedException{
挥发性t=新的挥发性();
t、 start();
睡眠(100);
t、 停止=新建ConcurrentHashMap();//写入引用
System.out.println(“In main:+t.stop);//读取引用
System.out.println(“等待运行完成”);
睡眠(200);
System.out.println(“仍在等待”);
t、 停下来。把(“a”,“b”);//写到地图上
睡眠(200);
系统出口(0);
}
公开募捐{
System.out.println(“运行中:+停止);//读取引用
while(stop==null){
}
System.out.println(“循环退出”);
}
}

我的印象是Doug Lea的并发对象可以通过data race安全地发布,因此即使被误用,它们也是“线程安全的”。虽然他可能不会公开宣传

不知道为什么,但我想,由于构造函数可能会使用某种内存障碍,它也会提供一些可见性保证——除了赋值是在构造函数之外完成的!我的想法是,第一次使用映射时,它将重新同步所有内容-这让我认为volatile并没有提供额外的保证。部分构造的对象参数是一个很强的参数。虽然我不确定concurrentHashMap是否可以部分构造,因为它的构造函数中可能会使用某种形式的同步。也许我们应该检查细节,但它们可能会变得非常混乱:),但我觉得这很可疑:
(int i=0;i
这个数组填充没有任何保护。哈,但他们在Java 7中修复了它!告诉过你它会变得混乱…:)好的,但这里你只是证明了引用可能永远不会被观察到。当通过数据竞赛发布时,这是常态。要证明你确实能观察到一个被撕裂的物体要困难得多。@MarkoTopolnik更新了一个put to the map,它仍然不能同步任何东西。是的,显示一个部分构建的地图会更难-甚至不知道如何做/在我的架构中是否可行。啊,这就是你的想法。但只要阅读线程不遵守参考