何时使用挥发性改性剂?

时间:2012-05-01 04:57:48

标签: java volatile

  

可能重复:
  When exactly do you use the volatile keyword in Java?

java中需要volatile修饰符的时间和原因?

我有兴趣看到易失性修改后的原语或对象引用的真实世界用法。

5 个答案:

答案 0 :(得分:6)

volatile修饰符将告诉JVM对并发运行的线程持谨慎态度。从本质上讲,volatile用于表示变量的值将被不同的线程修改。

声明一个易变的Java变量意味着:

  • 此变量的值永远不会在线程本地缓存:所有读取和写入将直接进入“主存储器”

  • 对变量的访问就好像它被包含在同步块中一样,自身同步。

我们在第二点说“好像”,因为至少对程序员来说(并且可能在大多数JVM实现中)没有涉及实际的锁对象。


volatile 修饰符告诉编译器,volatile修改的变量可能会被程序的其他部分意外更改。其中一种情况涉及多线程程序。

在多线程程序中,有时两个或多个线程共享同一个实例变量。出于效率考虑,每个线程可以保留这种共享变量的私有副本。

变量的实际(或主)副本会在不同时间更新,例如输入同步方法时。虽然这种方法工作正常,但有时可能效率低下。在某些情况下,所有真正重要的是变量的主副本始终反映其当前状态。

为了确保这一点,只需将变量指定为volatile,它告诉编译器它必须始终使用volatile变量的主副本(或者,至少始终保持所有私有副本与主副本保持同步,并且反之亦然)。此外,必须按照在任何私有副本上执行它们的准确顺序执行对主变量的访问。

答案 1 :(得分:6)

如果您正在使用多线程编程,那么volatile关键字将更有用。当多个 线程使用相同的变量,每个线程将拥有自己的该变量的本地缓存副本。所以,当它的时候 更新该值,它实际上在本地缓存中更新,而不是在主变量内存中。其他线程当中 使用相同的变量对另一个线程更改的值一无所知。为了避免这个问题, 如果将变量声明为volatile,则它不会存储在本地缓存中。每当线程更新时 值,它被更新到主存储器。因此,其他线程可以访问更新的值。

声明变量volatile意味着

  • 没有维护缓存意味着在主内存中进行了所有更改。
  • 访问此变量充当同步块,即使它处于同步单元中。

示例 -

public class Snippet implements Runnable{
volatile int num =0;

public void run(){
    Thread t = Thread.currentThread();
    String name = t.getName();
    if(name.equals("Thread1")){
        num=10;
    }
    else{
        System.out.println("value of num is :"+num);
    }

}
public static void main(String args[]) throws InterruptedException{
    Runnable r = new Snippet();
    Thread t1 = new Thread(r);
    t1.setName("Thread1");
    t1.start();

    Thread.sleep(1000);

    Thread t2 = new Thread(r);
    t2.setName("Thread2");
    t2.start();
}
 }

答案 2 :(得分:4)

(这个答案假定Java 5+ - 在此之前,volatile有较弱的保证。)

当你想要确保一个内存屏障,也就是一个正式的“发生在之前”关系时,它在一个字段的写入和一个单独的线程对该字段的后续读取之间是有用的。同步还可以为您提供这种关系,以及其他多线程保证,但它的速度稍慢,可能会产生同步瓶颈。

一个用例是并发集合类(如ConcurrentHashMapLinkedBlockingQueue),其中结合原子比较和设置(CAS)操作,您可以编写正确的线程 - 安全代码,无需使用synchronized

答案 3 :(得分:1)

volatile modifier告诉compiler您的程序的其他部分可能会意外更改由volatile修改的variable其中一种情况涉及多线程程序。
 在多线程程序中,有时两个或多个threads共享相同的instance variable。出于效率考虑,每个thread可以保留这种共享变量(in cache)的私有副本。 in ram的真实(或主版)副本(variable)会在不同时间更新,例如输入synchronized method时。  虽然这种方法工作正常,但有时可能效率低下。在某些情况下,所有真正重要的是变量的主副本始终反映其当前状态。要确保这一点,只需指定variable as volatile,它告诉compiler它必须始终使用volatile variable的主副本(不会保留缓存)。此外,必须按照在任何私人副本上执行主变量的准确顺序执行对主变量的访问 的 EX ...

public class snippet1 implements Runnable{
volatile int num =0;

public void run(){
    Thread t = Thread.currentThread();
    String name = t.getName();
    if(name.equals("Thread1")){
        num=10;
    }
    else{
        System.out.println("value of num is :"+num);
    }

}
public static void main(String args[]) throws InterruptedException{
    Runnable r = new snippet1();
    Thread t1 = new Thread(r);
    t1.setName("Thread1");
    t1.start();

    Thread.sleep(1000);

    Thread t2 = new Thread(r);
    t2.setName("Thread2");
    t2.start();
}

}

在上面的例子中如果你不使用volatile那么第二个线程的输出可能是0 但如果你把它变得不稳定,它会保证价值应该是10 ..

答案 4 :(得分:1)

第一个问题你得到了很好的答案。第二个:

  

任何人都能给我实时的情景吗

IMO,你永远不应该挥发。有更好的多线程应用程序工具。这种高级语言有这个关键字,这有点奇怪。 Here是一个很好的读物(它是关于C#的,但Java在这个问题上是相似的)。