有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java这种检测心跳的方法是线程安全和一致的吗?

这个问题已经在两篇博文(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;

它是否线程安全


共 (2) 个答案

  1. # 1 楼答案

    在java内存模型中?不,你不好

    我见过很多人试图采用这样的“软冲洗”方法,但如果没有明确的围栏,你肯定是在玩火

    中的“before”语义

    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中,运行时模型更强大,这种技巧是合法的,因为来自每个线程的单个写入都具有有序的可见性,所以在尝试从中翻译任何示例时要小心

  2. # 2 楼答案

    我不这么认为

    第一个if语句:

    if (counter == HeartBeatThread.counter) 
        return;
    

    不访问任何易失性字段,且未同步。因此,您可能会永远读取过时的数据,永远不会访问volatile字段

    引用第二篇博文中的一条评论:“线程A在写入易失性字段f时可以看到的任何内容,在线程B读取f时都可以看到。” 但在你的情况下,B(客户机)从不读取f(=缓存刷新)。所以改成了HeartBeatThread。计数器不必对客户可见