2013-06-27 96 views
-1

不使用volatile关键字平台特定的效果吗?是否使用volatile关键字平台特有的效果?

在Ubuntu 13.04 x64上使用openJDK 1.7使用或不使用volatile关键字没有任何影响;意思是程序在不使用volatile时按预期执行。

我想知道这个的确切原因是什么,以及为什么它不会像Windows的Oracle JVM那样每次都失败。

我知道什么是挥发性担保以及何时应该使用。这不是问题。

例如:

public class VolatileTest { 
private static boolean test; 
public static void main(String... args) { 
    Thread a = new Thread(new Runnable() { 

     @Override 
     public void run() { 
      try { 
       Thread.sleep(3000); 
      } catch (InterruptedException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 

      test = true; 

     } 
    }); 

    Thread b = new Thread(new Runnable() { 
     @Override 
     public void run() { 
      while(!test) { 
       try { 
        Thread.sleep(500); 
       } catch (InterruptedException e) { 
        // TODO Auto-generated catch block 
        e.printStackTrace(); 
       } 
       System.out.println(test); 
      } 
     } 
    }); 

    a.start(); 
    b.start(); 
}} 
+0

你是怎么测试它并在我们这里证明它的? –

+0

示例代码? Java本身,所有这些都应该是平台独立的。 – Ayman

+1

也许'volatile'并不是必需的:) – dasblinkenlight

回答

1

不使用volatile关键字平台具体的影响?

Java内存模型描述了一个程序必须的行为,当它被正确sychronized,但并没有说太多关于程序如何可能的行为时,它不是(除了例如因果关系要求) 。所以理论上讲,这是平台特定的。

实际上,它是平台和JVM特定的。通常,x86架构具有相当强大的内存模型,删除关键字通常不会破坏您的程序(即代码仍然表现为变量仍然为volatile)。

某些(通常较旧的)处理器甚至是连续一致的,这意味着您的程序将表现得好像一切都是同步的。另一方面,在诸如ARM之类的内存模型较弱的处理器上,观察多线程程序破坏的效果更容易。类似地,一些JVM在其优化方面更具侵略性,并且会不同地重新排序指令,提升变量等。对于给定的JVM,您使用的参数也可能有影响。例如,在Hotspot上,如果禁用JIT编译,您可能会“修复”一些线程安全问题。请参阅this post on memory models

3

挥发性不在其影响的特定平台。它符合Java Memory Model.中规定的规范,其中的实现在JVM中对于每个平台来说明显不同,至于它如何强制执行内存屏障。

你能提供样本代码吗?你的测试可能是误导性的。

+0

我已经添加了一个小例子。 –

+0

另外,它不是一个编译器优化?!所以,JVM应该不重要。 –

+2

也许值得注意的是,尽管'volatile'的效果本身并不是特定于平台的,但是在必要的地方省略volatile的效果可能是平台特定的。 –

1

一个字段可能被声明为volatile,在这种情况下,Java内存模型确保 所有线程都看到变量的一致值。 - JLS 8.3.1.4

挥发性保证变量的一致视图。 volatile的本地实现禁止CPU/Core将变量保存在线程所执行的计算的寄存器中,以便运行在其他CPU/Core上的所有线程都可以具有该变量的一致视图。

您不能通过运行少量测试用例得出结论。这些问题可能会在成千上万的测试中被发现。

0

声明一个字段易失性确保所有线程都能看到字段的当前值。不声明一个字段为volatile并不能确保其他线程不会看到线程所做字段的修改。

修改一点点你的例子:

public class VolatileTest { 

private static boolean test = false; 

public static void main(String... args) { 
    Thread a = new Thread(new Runnable() { 

     @Override 
     public void run() { 

      boolean localTest = test; 
      try { 
       Thread.sleep(3000); 
      } catch (InterruptedException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 

      System.out.println("changing local test"); 
      test = !localTest; 

     } 
    }); 

    Thread b = new Thread(new Runnable() { 
     @Override 
     public void run() { 
      boolean localTest = test; 
      while(!localTest) { 
       localTest = test; 
      } 
     } 
    }); 

    a.start(); 
    b.start(); 
}} 

这人会阅读领域test了很多次,那么JVM将缓存线程btest值。制作线程b的睡眠将使得test字段不会被读取太多,那么JVM不会尝试“优化”(在线程b上缓存test的值) - 但是如果test被声明为volatile,JVM将无法缓存test的值。

测试在Ubuntu Lucid 64位上运行,使用Oracle的Java 1.7.0。

0

您提供的示例代码没有足够的循环来获得JIT优化。它将在解释器中运行,因此您的代码(很可能)不会受到任何编译器优化技巧的影响,从而导致其失败。 CPU本身也不太可能拉高到足以阻止这些代码的技巧,特别是考虑到这些睡眠中涉及的相对巨大的时间表。

但是,这是您的特定JVM的实现细节。即使该错误永远不会显示在您的硬件,JVM和操作系统的特定组合上,您的程序仍然很活泼。

0
  • 为了性能的优化,编译器& JVM实现可移动的代码顺序,还可以使用寄存器或高速缓存到只有一个线程可见。很明显,这种行为对于每个实现都不相同。
  • 这意味着一个变量的更新值对一个线程是可见的;而所有其他线程读取陈旧的varlue
  • 如果要安全地访问多个线程中的变量,以确保一致读取最新的值,则must使用volatile。 volatile提供的这种行为不是特定于平台的。
  • 您可能在某些情况/实施下侥幸它,并看到一致的最新值,但没有volatile,但这是锅运气&无法编程。