3

可能重复:
你什么时候在 Java 中使用 volatile 关键字?

java中何时以及为什么需要volatile修饰符?

我有兴趣看到 volatile 修改后的原语或对象引用在现实世界中的用法。

4

5 回答 5

10

volatile 修饰符将告诉 JVM 小心并发运行的线程。本质上, volatile 用于指示变量的值将被不同的线程修改。

声明一个 volatile Java 变量意味着:

  • 这个变量的值永远不会被缓存到线程本地:所有的读写都将直接进入“主内存”

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

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


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

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

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

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

于 2012-05-01T05:00:16.250 回答
9

如果您正在使用多线程编程,那么 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();
}
 }
于 2012-05-01T05:34:10.473 回答
4

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

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

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

于 2012-05-01T05:13:11.810 回答
2

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

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。
但是如果您将其设置为 volatile,则它可以保证该值应为 10..

于 2012-05-01T05:20:18.110 回答
1

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

谁能给我它的实时场景

海事组织,你永远不应该变态。多线程应用程序有更好的工具。这么高级的语言居然有这个关键字,有点奇怪。是一本很好的读物(它是关于 C#,但 Java 在这件事上是相似的)。

于 2012-05-01T05:33:05.360 回答