【问题标题】:Cheap read-write lock with hashmap带有 hashmap 的廉价读写锁
【发布时间】:2020-07-28 15:52:01
【问题描述】:
 static volatile Map currentMap = null;   // this must be volatile
static Object lockbox = new Object();  
 
public static void buildNewMap() {       // this is called by the producer     
    Map newMap = new HashMap();          // when the data needs to be updated
 
    synchronized (lockbox) {                 // this must be synchronized because
                                         // of the Java memory model
      // .. do stuff to put things in newMap
      newMap.put(....);
      newMap.put(....);
   }                 
/* After the above synchronization block, everything that is in the HashMap is 
   visible outside this thread */
 
/* Now make the updated set of values available to the consumer threads.  
   As long as this write operation can complete without being interrupted, 
   and is guaranteed to be written to shared memory, and the consumer can 
   live with the out of date information temporarily, this should work fine */
 
    currentMap = newMap;
 
}
public static Object getFromCurrentMap(Object key) {
    Map m = null;
    Object result = null;
 
    m = currentMap;               // no locking around this is required
    if (m != null) {              // should only be null during initialization
      Object result = m.get(key); // get on a HashMap is not synchronized
     
      // Do any additional processing needed using the result
    }
    return(result);
 
}

这是本文https://www.ibm.com/developerworks/library/j-hashmap/index.html的代码示例 我仍然不明白为什么我们需要在 buildNewMap 方法中使用同步块。除了 currentMap = newMap;做。 当我们在 m = currentMap; 处读取地图参考时;我们依赖 volatile 读写,读取线程甚至不知道生产者线程中的同步......

【问题讨论】:

    标签: java concurrency synchronized


    【解决方案1】:

    如果 hashmap 仅在写入 'currentMap' 之前被修改,则它的内容保证对其他线程可见。这是因为在写入地图内容和写入 currentMap(程序顺序)之间有一个发生在边缘之前;并且在读取concurrentMap之间有一个happens-before边缘(易失性变量),并且在读取变量和读取内容(程序顺序)之间有一个happens before edge。由于happens before是可传递的,因此在写入内容和读取内容之间存在一个happens beforge边缘。

    同步块似乎没有任何用途。

    【讨论】:

      【解决方案2】:

      根据这篇文章,Java 内存模型为易失性写入提供了强有力的保证:

      http://tutorials.jenkov.com/java-concurrency/volatile.html

      特别是:

      • 如果线程 A 写入 volatile 变量,而线程 B 随后读取相同的 volatile 变量,则线程 A 在写入 volatile 变量之前可见的所有变量,在线程 B 读取 volatile 变量后也将可见。
      • 如果线程 A 读取 volatile 变量,则线程 A 在读取 volatile 变量时可见的所有变量也将从主内存中重新读取。

      所以看起来同步块是不必要的。

      【讨论】:

      • 但是同步不是一个双向的过程吗?如果一个线程在同步块下发布某些内容,而其他进行读取的线程应该使用相同的监视器才能看到发布线程完成的“工作”?
      • 同步块完成后,同步块中所做的更改对所有线程都是可见的。这本质上是使用同步块作为内存屏障,而不是同步机制。
      • 但是 volatile 也会产生内存屏障,为什么我们还需要一个?
      • 到目前为止,我认为 volatile 只保证 volatile 变量的可见性,但据此,它比这要强得多:tutorials.jenkov.com/java-concurrency/volatile.html 根据这篇文章,您似乎是对的:不需要同步块。
      猜你喜欢
      • 1970-01-01
      • 2016-02-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-25
      • 2010-10-04
      • 2011-12-04
      • 1970-01-01
      相关资源
      最近更新 更多