Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/369.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 我们需要使ConcurrentHashMap易失性吗?_Java_Concurrency_Java.util.concurrent - Fatal编程技术网

Java 我们需要使ConcurrentHashMap易失性吗?

Java 我们需要使ConcurrentHashMap易失性吗?,java,concurrency,java.util.concurrent,Java,Concurrency,Java.util.concurrent,我们有一个共享的ConcurrentHashMap,它由两个线程读写 class Test { private ConcurrentHashMap<Object, Object> map = new ConcurrentHashMap<>(); Object read() { return map.get(object); } void write(Object key, Object object) {

我们有一个共享的
ConcurrentHashMap
,它由两个线程读写

class Test {
    private ConcurrentHashMap<Object, Object> map = new ConcurrentHashMap<>();

    Object read() {
        return map.get(object);
    }

    void write(Object key, Object object) {
        map.put(key, object);
    }
}
类测试{
私有ConcurrentHashMap=新ConcurrentHashMap();
对象读取(){
返回map.get(对象);
}
无效写入(对象键、对象){
放置(键、对象);
}
}
我们是否需要使映射具有易变性,以便读线程尽快看到一个线程的写操作

是否有可能一个线程中的put-to-map没有被另一个线程看到或很晚才被另一个线程看到

HashMap

的相同问题在语义读取和写入相应变量之前也会出现

字段可以声明为volatile,在这种情况下是Java内存模型 确保所有线程都能看到变量的一致值 (§17.4)

它与变量值引用的对象无关。您没有修改变量,因此不应该*有任何问题,除非(*)您没有安全地发布跨线程共享的
Test
对象

假设您没有采取正确的预防措施,通过
final
volatile
或其他一些同步机制,JMM允许在对象的构造函数完全初始化之前提供对对象的引用。因此,您的一个线程可以在初始化
map
字段之前尝试使用该字段(例如,它将看到
null
)。从这个意义上讲,密码可能会被破解

是否有可能一个线程中的put-to-map没有被另一个线程看到或很晚才被另一个线程看到

这是不可能的,因为状态,
ConcurrentHashMap
方法引入了适当的内存屏障

检索反映最近完成的更新的结果 手术一开始就进行。(更正式地说,是更新 给定键的操作在与任何 (非空)检索报告更新值的键


但是,
HashMap
不是一种线程安全类型。
volatile
在这里也没有帮助,因为它控制变量的更改,而不是变量引用的对象。您需要外部同步来保护
put
get
HashMap

的调用(如果可以
final
然后这样做。如果您不能使其成为
最终的
,那么您需要使其成为
易失的
易失的
应用于字段分配,如果它不是
最终的
,则有可能(至少根据JMM)一个线程对CHM字段的写入可能对另一个线程不可见。重申一下,这是
ConcurrentHashMap
字段分配,不使用CHM

话虽如此,你真的应该让它成为最后的

我们是否需要使映射具有易变性,以便读线程尽快看到一个线程的写操作

如果您提到的写入是使用CHM本身的变异方法完成的(如
put
remove
),那么使字段不稳定不会产生任何效果。所有内存可见性保证都是在CHM内完成的

是否有可能一个线程中的put-to-map没有被另一个线程看到或很晚才被另一个线程看到?对于
HashMap
,同样的问题


不适用于
ConcurrentHashMap
。如果同时使用普通
HashMap
,请不要使用。请参阅:

这里有两个子问题:对映射的引用的可见性和写入映射的值的可见性

  • 参考地图的可视性:

    我们需要制作地图吗
  • 应该关注多线程环境中引用的安全发布

  • 通过正确锁定的字段访问参考(JLS 17.4.5)
  • 使用静态初始化器进行初始化存储(JLS 12.4)(实际上不是我们的情况)
  • 通过volatile字段(JLS 17.4.5)提供对引用的访问,或者根据本规则,通过AtomicX类(如AtomicReference)提供对引用的访问
  • 将该值初始化为最终字段(JLS 17.5)
  • 因此,在您的情况下,您的“映射”引用未正确发布。这可能会导致Test.read()或/和Test.write()中出现NullPointerException(这取决于哪个线程实例化ConcurrentHashMap并将其放入“映射”字段)。正确的代码应为以下代码之一:

    //1. Provide access to the reference through a properly locked field
    class Test {
    
        ConcurrentHashMap map;
    
        synchronized void init(ConcurrentHashMap map) {
            this.map = map;
        }
    
        synchronized void read() {
            map.get(object);
        }
    
        synchronized void write() {
            map.put(key, object);
        }
    }
    
    // or
    class Test {
        ReadWriteLock rwl = new ReentrantReadWriteLock();
    
        ConcurrentHashMap map;
    
        void init(ConcurrentHashMap map) {
            rwl.writeLock().lock();
            this.map = map;
            rwl.writeLock().release();
        }
    
        void read() {
            rwl.readLock().lock();
            try {
                map.get(object);
            } finally {
              rwl.readLock().release();
            }
        }
    
        void write() {
            rwl.writeLock().lock();
            try {
                map.put(key, object);
            } finally {
                rwl.writeLock().release();
            }
        }
    }
    
    // 3. Provide access to the reference via a volatile field
    class Test {
    
        volatile ConcurrentHashMap map; // or AtomicReference<ConcurrentHashMap> map = new AtomicReference();
    
        void init(ConcurrentHashMap map) {
            this.map = map;
        }
    
        void read() {
            map.get(object);
        }
    
        void write() {
            map.put(key, object);
        }
    }
    
    // 4. Initialize the value as a final field
    class Test {
    
        final ConcurrentHashMap map;
    
        Test(ConcurrentHashMap map) {
            this.map = map;
        }
    
        void read() {
            map.get(object);
        }
    
        void write() {
            map.put(key, object);
        }
    }
    
    //1.通过正确锁定的字段提供对引用的访问
    课堂测试{
    ConcurrentHashMap;
    同步void init(ConcurrentHashMap映射){
    this.map=map;
    }
    同步无效读取(){
    map.get(对象);
    }
    同步无效写入(){
    放置(键、对象);
    }
    }
    //或
    课堂测试{
    ReadWriteLock rwl=新的ReentrantReadWriteLock();
    ConcurrentHashMap;
    void init(ConcurrentHashMap映射){
    rwl.writeLock().lock();
    this.map=map;
    rwl.writeLock().release();
    }
    无效读取(){
    rwl.readLock().lock();
    试一试{
    map.get(对象);
    }最后{
    rwl.readLock().release();
    }
    }
    无效写入(){
    rwl.writeLock().lock();
    试一试{
    放置(键、对象);
    }最后{
    rwl.writeLock().release();
    }
    }
    }
    //3.通过易失性字段提供对参考的访问
    课堂测试{
    volatile ConcurrentHashMap;//或AtomicReference map=new AtomicReference();
    void init(ConcurrentHashMap映射){
    this.map=map;
    }
    空隙率