为什么要在单线程方法中放置同步块?

Why would I place a synchronized block within a single-threaded method?

我在 IBM - developerworks 上偶然发现 this article,他们发布的代码让我提出了一些问题:

  1. 为什么局部变量 Map 的构建包含在 synchronized 块中?请注意,他们含蓄地说只有一个 producer 线程。

  2. 实际上,为什么这个片段需要一个 synchronized 块?一个 volatile 变量就足够了,因为新创建的地图只有在填满后才会发布。

  3. 锁对象上只有一个线程synchronizing有什么意义?

文章提到:

The synchronized block and the volatile keyword in Listing 1 are required because no happens-before relationship exists between the writes to the currentMap and the reads from currentMap. As a result, reading threads could see garbage if the synchronized block and the volatile keyword were not used.

代码中的注释是:

this must be synchronized because of the Java memory model

我觉得我正在处理超出我理解范围的多线程概念;我希望有更多专业知识的人为我指明正确的方向。


这是从文章中摘录的片段:

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);

}

根据 Java memory model,易失性写入和后续易失性读取之间存在 happens-before 关系,这意味着 m = currentMap;保证看到 currentMap = newMap; 之前发生的一切,不需要 synchronized 块。

不仅如此,它什么也没做,:

this must be synchronized because of the Java memory model

After the above synchronization block, everything that is in the HashMap is visible outside this thread

评论有误。根据 Java 内存模型,只有当两个线程都是 synchronized 时,才会存在 happens-before 关系;根据 JMM (some JVM 实现,按照文章中的方式使用它绝对没有任何作用,也许 2007 年的某些 IBM JVM 实现在这种情况下执行了某种同步,但 JMM 不需要它)。

总之,IBM 的文章是完全错误的。