2008-11-07 47 views
3

此问题已在两篇博文(http://dow.ngra.de/2008/10/27/when-systemcurrenttimemillis-is-too-slow/,http://dow.ngra.de/2008/10/28/what-do-we-really-know-about-non-blocking-concurrency-in-java/)中讨论过,但我还没有听到明确的答案。如果我们有一个线程,这是否:运行这是检测心跳线程安全和一致的方式吗?

public class HeartBeatThread extends Thread { 
    public static int counter = 0; 
    public static volatile int cacheFlush = 0; 

    public HeartBeatThread() { 
    setDaemon(true); 
    } 

    static { 
    new HeartBeatThread().start(); 
    } 

    public void run() { 
    while (true) {  
     try { 
     Thread.sleep(500); 
     } catch (InterruptedException e) { 
     throw new RuntimeException(e); 
     } 

     counter++; 
     cacheFlush++; 
    } 
    } 
} 

而且很多客户的以下内容:

if (counter == HeartBeatThread.counter) return; 
counter = HeartBeatThread.cacheFlush; 

是线程安全与否?

回答

5

在java内存模型中?不,你不行。

我已经看到许多企图朝这种“非常软”的方式努力,但没有明确的围栏,你绝对会玩火。

http://java.sun.com/docs/books/jls/third_edition/html/memory.html#17.7

开始语义学“之前发生”来指纯粹的线程间行动截至17.4.2结束“动作”。这导致了很多混淆,因为在这之前他们区分了线程间和线程间动作。因此,操作计数器的内部线程操作并没有通过之前发生的关系在易失性操作中显式同步。你有两个关于同步的推理线索,一个管理本地一致性,并受制于别名分析等所有漂亮的技巧来洗牌操作。另一个关于全局一致性,并且仅针对线程间操作进行定义。

一种用于线程内逻辑的内部线程逻辑,它在线程内表示读取和写入一致地重新排序,另一种用于说明像易失性读取/写入之类的事物的线程间逻辑,并且同步开始/结束被适当地隔离。

问题是非易失性写入的可见性未定义,因为它是线程内操作,因此不在规范中。它运行的处理器应该能够看到它,因为它是按顺序执行这些语句的,但它为线程间目的执行的序列化可能是未定义的。

现在,这是否会影响你的现实是另一回事。

在x86和x86-64平台上运行java?从技术上讲,你处于一片黑暗的领域,但实际上非常强大的保证了读写的x86位置,包括整个访问cacheflush时的读/写总次序和两次写入的本地次序以及两次读取应该启用此代码正确执行,只要它通过编译器不受干扰地执行。这假定编译器不会介入并尝试使用标准允许的自由度,因为两个线程内操作之间可证明缺少别名,所以可以重新排序操作。

如果您移动到像ia64这样的发布语义较弱的内存?然后你回归自己。但是,编译器可以完全真诚地在java中在任何平台上打破这个程序。它现在的功能是当前标准实现的人为因素,而不是标准。另外,在CLR中,运行时模型更强大,这种技巧是合法的,因为每个线程的单个写入都具有有序的可见性,所以要小心地尝试从那里翻译任何示例。

1

嗯,我不认为这是。

第一个if语句:

if (counter == HeartBeatThread.counter) 
    return; 

不访问任何挥发性场不同步。所以你可能会永远读取陈旧的数据,永远不会访问volatile字段。

从第二个博客条目中的某条评论引用:“当线程A写入易失性字段f时,对线程B可见的任何内容在读取f时对线程B可见。” 但在你的情况B(客户端)永远不会读取f(= cacheFlush)。因此,对HeartBeatThread.counter的更改不必变得对客户端可见。