volatile是一个变量修饰符,而synchronized修饰一段代码块和方法。
我们来看下下面的代码:
int i1 ;
int geti1(){ return i1 ;}
volatile int i2 ;
int geti2(){ return i2 ;}
int i3 ;
synchronized int geti3(){ return i3 ;}
在这里,我们定义了三个整型变量:i1 ,i2,和i3 。而且,我们定义了三种相应的getter方法:geti1(),geti2(),和geti3()。
geti1()访问i1当前线程中当前存储的值 。
线程可以具有变量的本地副本,并且数据不必与其他线程中保存的数据相同。特别是,另一个线程可能已将i1 在其线程中更新,但当前线程中的值可能与该更新值不同。实际上,Java演示了“主”内存的概念,而这是保存变量当前“正确”值的内存。线程可以拥有自己的变量数据副本,并且线程副本可以与“主”内存不同。
geti2() 从“主”存储器有效地访问i2的值。一个volatile 变量是不允许有一个变量是目前在“主”内存的值不同的本地副本。实际上,volatile声明的变量必须使其数据在所有线程之间同步,这样无论何时在任何线程中访问或更新变量,所有其他线程都会立即看到相同的值。通常,volatile变量具有比“普通”变量更高的访问和更新开销。典型的,允许线程拥有自己的数据副本,这样可以提高效率。
volatile和synchronized之间有两点不同:
首先,synchronized 取并释放监视器上的锁定,这些锁定强制一次只能一个线程执行代码块。这是synchronized一个相当着名的方面。但 synchronized也要同步内存。实际上,synchronized 是将整个线程内存与“主”内存同步。因此,执行geti3()时做了以下操作:
- 线程获取对象this的监视器上的锁 。
- 线程内存刷新所有变量,即它的所有变量都有效地从“主”内存中读取。
- 代码块被执行。在这种情况下,这意味着将返回值设置为当前i3的值,该值可能刚刚从“主”存储器复位。
- 对变量的任何更改通常都会写入“主”内存,但是对于geti3(),我们没有任何更改。
- 线程释放监视器上的对象this的锁。
因此,volatile只在线程内存和“主”内存之间同步一个变量的值,synchronized同步线程内存和“主”内存 之间所有变量的值,并锁定和释放监视器以控制多个线程之间的所有权。
从这些信息可以得出结论,synchronized 可能比volatile有更多的开销。