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

dev*_*ull 34 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;}
    
    }
    
    Run Code Online (Sandbox Code Playgroud)

class TestClass
{  private volatile int x;

   int get(){return x;}
   void set(int x){this.x = x;}

}
Run Code Online (Sandbox Code Playgroud)

Ker*_*ğan 60

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

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

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

     int i3;
     synchronized int geti3() {return i3;}
Run Code Online (Sandbox Code Playgroud)

geti1()访问i1当前线程中当前存储的值.线程可以有变量的本地副本,并且数据不必与其他线程中保存的数据相同.特别是,另一个线程可能已i1在其线程中更新,但当前线程中的值可能与此不同更新的价值.事实上,Java有一个"主"内存的概念,这是保存变量当前"正确"值的内存.线程可以拥有自己的变量数据副本,并且线程副本可以与"主"内存不同.所以,事实上,它有可能为"主"内存有一个价值1i1,i13i1,如果线程1线程2具有各自的i1,但这些更新的价值尚未被传播到"主"内存或其他线程.

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

volitile和synchronized之间有两个不同之处.

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

  1. 线程获取监视器上的对象锁定.
  2. 线程内存刷新所有变量,即它的所有变量都有效地从"主"内存中读取.
  3. 执行代码块(在这种情况下,将返回值设置为i3的当前值,该值可能刚刚从"main"存储器复位).
  4. (对变量的任何更改现在通常都会写入"主"内存,但对于geti3(),我们没有任何更改.)
  5. 该线程释放监视器上的锁定对象.

因此,volatile只在线程内存和"主"内存之间同步一个变量的值,synchronized会同步线程内存和"主"内存之间所有变量的值,并锁定和释放监视器以启动.明确同步可能比volatile更具开销.

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

  • 关于同步只是一个变量的声明在这里并不是真的,通常所写的所有内容都听起来并不完全正确.写入volatile与监视器版本具有相同的记忆效应.从volatile获取具有与监视器获取操作相同的记忆效应.所有这些意味着对volatile变量的访问也会影响其他变量.此外,在"易失性流程"中提到的变量上没有像块一样,有写入内存和高速缓存失效. (4认同)

Ste*_*Kuo 6

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