本文介绍了Java中一些基本但很重要的概念。
volatile
是一个域修饰器(field modifier),而synchronized
修饰代码块和方法。
因此,我们可以使用这两个关键字来指定一个简单累加器的三种变体。
int i1;
int geti1() { return i1; }
volatile int i2;
int geti2() { return i2; }
int i3;
synchronized int geti3() { return i3; }
上面,我们定义了3个整数变量:i1
,i2
和i3
。并且,我们定义了3个对应的getter方法:geti1()
,geti2()
和geti3()
。
geti1()
在当前线程访问当前存储在i1
中的值。
线程可能有变量的本地拷贝,并且数据不保证与其它线程中保存的数据相同。特别是,其它线程可能在它的线程中已经更新了i1
,但是在当前线程中这个值可能不同于那个更新后的值。事实上,Java演示了“主内存”的想法,对于变量,它保存当前“正确的”值。线程可能有它们自己的变量数据拷贝,并且,线程的拷贝可能不同于“主内存”。
因此,它是可能的,在“主内存”中i1
的值是1,在线程1中i1
的值是2,并且如果线程1和线程2都更新了i1
,则在线程3中i1
的值是3。但是,更新后的值并未被传播到“主内存”或是其它线程。
另一方面,geti2()
从“主内存”中有效地访问i2
的值。一个volatile
变量不允许有线程的本地拷贝,这会导致该变量与“主内存”中当前保存的值不同。实际上,声明为volatile
的变量必须在所有线程之间同步其数据,这样无论何时你在任何线程中访问或更新该变量,其它所有线程都会立即看到相同的值。一般来说,volatile
变量有更高的访问和更新开销相比于普通变量。通常,为了提高效率,线程允许有它们自己的数据拷贝。
volatile
和synchronized
主要有两点差异:
首先,synchronized
通过在监视器上获取和释放锁,来强制一次只能有一个线程执行一个代码块。这是synchronized
的一个众所周知的方面。但是synchronized
也同步内存。实际上,synchronized
将整个线程内存和“主内存”同步。因此,运行geti3()
会执行以下操作:
- 线程获取对象
this
的监视器上的锁; - 线程内存刷新其所有变量,即其所有变量均有效地从“主内存”读取;
- 代码块被运行。在这种情形下,这意味着设置
i3
的当前值为返回值,它可能刚刚从“主内存”中重置; - 任何对变量的改变一般会被写入到“主内存”,但是对于
geti3()
,我们没有改变; - 线程释放对象
this
的监视器上的锁。
因此,volatile
仅同步线程内存和“主内存”之间一个变量的值,synchronized
同步线程内存与“主内存”之间所有变量的值,并且通过加解监视器上的锁以控制在多个线程之间的所有权。
从这个信息可以得出结论:synchronized
相比于volatile
可能有更多的开销。
原文:Difference Between Volatile and Synchronized Keywords in Java