前段时间面试时遇到这样一个问题:使用volatile修饰int型变量i,多个线程同时进行i++操作,这样可以实现线程安全吗?
我感觉是不可以的,但是又说不出来为什么。下来后翻看了许多资料,终于了解了volatile的含义和用法了,一起来看看吧。
volatile是变量修饰符,其修饰的变量具有可见性。在Java中为了加快程序的运行效率,对一些变量的操作通常是在寄存器或是cpu缓存上进行的,之后才会同步到内存中,而加了volatile修饰符的变量则是直接读写内存。可见性也就说一旦某个线程修改了该变量,其他线程读值时可以立即获取修改之后的值。
synchronized则作用于一段代码或方法,使用了该修饰符既可以保证可见性,也能够保证原子性。可见性表现在虽然其修饰的代码段或方法里面的读写操作可能在cpu缓存上进行的,不过在出代码段或方法前会把缓存中的数据同步到内存中。原子性表现在要么不执行,要么执行到底。
x = 10; //语句1
y = x; //语句2
x++; //语句3
x = x + 1; //语句4
这四个语句哪些是原子操作?其实只有语句1是原子操作,怎么样,想不到吧?^^下面我来解释下
语句1就是1个动作,把10写入到内存。
语句2两个动作,先读取x的值,然后再写入内存。
语句3,4是一样的,有三个动作,读取x的值,计算,写入内存。
知道了这些,那么开头的问题就好理解了,比如有两个线程A和B对volatile修饰的i进行++操作,i的初始值是0,A线程执行i++时刚读取了i的值0,就切换到B线程了,B线程(从内存中)读取i的值也为0,然后就切换到A线程继续执行i++操作,完成后i就为1了,接着切换到B线程,因为之前已经读取过了,所以继续执行i++操作,最后的结果i就为1了。
从这里可以看出volatile虽然具有可见性但是并不能保证原子性。
volatile一般是用来作为状态标志的,看个我在java中如何结束线程 一文中举的例子:
使用synchronized主要是用来保证线程安全的,看一个经典的单例模式(双重校验锁):
synchronized的另一种用法是这样的(懒汉模式):
(哇塞volatile跟synchronized同台了耶!)
最后再来两篇扩展,写的都很棒!
1、volatile关键字解析,从内存模型,并发中的关键概念,讲到volatile的使用场景 点击打开链接
2、多线程同步的五种方法 点击打开链接