Java中的易失性关键字 - 澄清

时间:2010-08-30 18:30:54

标签: java volatile

我对于我在java中读到有关volatile关键字的应用程序的内容感到很困惑。

  1. 以下陈述是否正确? “在对同一字段的每次后续读取之前发生对易失性字段的写入”

  2. 理想情况下,什么时候应该使用volatile关键字?

  3. 有什么区别:

    class TestClass
    {  private int x;
    
       synchronized int get(){return x;}
       synchronized void set(int x){this.x = x;}
    
    }
    
  4. class TestClass
    {  private volatile int x;
    
       int get(){return x;}
       void set(int x){this.x = x;}
    
    }
    

6 个答案:

答案 0 :(得分:60)

volatile 字段修饰符,而同步修改代码块方法。因此,我们可以使用这两个关键字指定简单访问器的三种变体:

     int i1;
     int geti1() {return i1;}

     volatile int i2;
     int geti2() {return i2;}

     int i3;
     synchronized int geti3() {return i3;}

geti1()访问当前线程中当前存储在i1中的值。 线程可以有变量的本地副本,并且数据不必与其他线程中保存的数据相同。特别是,另一个线程可能在其线程中更新了i1,但是当前线程中的值可能与更新的值不同。事实上,Java有一个“主”内存的概念,这是保存变量当前“正确”值的内存。线程可以拥有自己的变量数据副本,并且线程副本可以与“主”内存不同。事实上,对于i1,“主”内存可能具有 1 的值,因为thread1的值为 2 {如果 thread1 thread2 thread2 的值为 3 i1 >已更新i1但这些更新值尚未传播到“主”内存或其他线程。

另一方面,i1有效地从“主”内存中访问geti2()的值。不允许volatile变量具有与“main”内存中当前保存的值不同的变量的本地副本。实际上,声明为volatile的变量必须使其数据在所有线程之间同步,因此无论何时在任何线程中访问或更新变量,所有其他线程都会立即看到相同的值。通常,volatile变量比“plain”变量具有更高的访问和更新开销。通常,允许线程拥有自己的数据副本以提高效率。

volitile和synchronized之间有两个不同。

首先同步获取并释放监视器上的锁定,这些锁定一次只能强制一个线程执行代码块。这是同步的众所周知的方面。但同步也同步内存。事实上,synchronized将整个线程内存与“主”内存同步。因此执行i2会执行以下操作:

  1. 线程获取监视器上的对象锁定。
  2. 线程内存刷新所有变量,即它的所有变量都有效地从“主”内存中读取。
  3. 执行代码块(在这种情况下,将返回值设置为i3的当前值,该值可能刚刚从“main”内存中重置。)
  4. (对变量的任何更改通常都会写入“主”内存,但对于geti3(),我们没有任何更改。)
  5. 线程释放监视器上的锁定对象。
  6. 因此,volatile只在线程内存和“主”内存之间同步一个变量的值,synchronized会同步线程内存和“主”内存之间所有变量的值,并锁定并释放一个监视器来启动。明确同步可能比volatile更具开销。

    http://javaexp.blogspot.com/2007/12/difference-between-volatile-and.html

答案 1 :(得分:6)

volatile保证从变量读取始终反映最多更新值。运行时可以通过各种方式实现此目的,包括在值发生更改时不缓存或刷新缓存。

答案 2 :(得分:2)

bwawok躲过了它,但volatile关键字不仅仅用于内存可见性。在Java 1.5发布之前,volatile关键字声明该字段将通过每次读取和刷新写入时命中主内存来获取对象的最新值。

今天的volatile关键词syas是两件非常重要的事情:

  1. 不要担心如何但是要知道在阅读volatile字段时,您将始终拥有最新的值。
  2. 编译器无法重新命令易失性读/写以维持程序顺序。

答案 3 :(得分:0)

从客户端的角度来看,私有volatile字段对公共接口是隐藏的,而同步方法更明显。

答案 4 :(得分:0)

回答问题的第3部分,部分回答第2部分。

synchronizedvolatile个样本之间没有功能差异。

然而,每个人在性能方面都有自己的缺点。在某些情况下,volatile的效果可能比仅使用synchronized或来自java.util.concurrent的其他原语更糟糕。有关此内容的讨论,请参阅 - > Why aren't variables in Java volatile by default?

答案 5 :(得分:0)

回答 KeremBaydoğan 是完全正确的。我只是想举例说明volatile关键字为我们提供的内容。

首先,我们有一个计数器,像

一样
public class Counter {
    private int x;
    public int getX() { return x; }
    public void increment() { x++; }
}

一些Runnable任务增加了x

的值
@Override
public void run() {
    for (N) {
            int oldValue = counter.getX();
            counter.increment();
            int new value = counter.getX();
        }

    }
}

如果没有同步,则会有interference between threads并且根本无法正常工作

解决此问题的最简单方法:

public class Counter {
    private int x;
    public synchronized int getX() { return x; }
    public synchronized void increment() { x++; }
}

实际上为了强制系统中断,我在阅读和写作Thread.sleep之前做了x,想象一下是BD还是一项需要处理的大任务。


现在,volatile对什么有用?那里有很多好文章:volatile articlethis question

同步对公共资源的访问是答案,但是保持标志停止线程

是一个不错的选择

我是我们的上一个。例如,假设我们想要将变量增加到100,简单的方法可能是volatile boolean标志。例如:

private volatile boolean stop;

@Override
public void run() {
    while(!stop) {
        int oldValue = counter.getX();
        if (oldValue == 100) {
             stop = true;
        } else {
             counter.increment();
             int new value = counter.getX();
        }       
     }
}

这样可以正常使用,但是,如果您从标记中删除volatile关键字,则可能会遇到无限循环。