volatile仅仅用来保证该变量对所有线程的可见性,但不保证原子性。
一开始接触到volatile,百度或者书本上都会说这句话.前半句话的意思是:在volatile读之前,总能看到任意线程对这个变量的最后写入。
后半句不保证原子性指的是:不保证符合操作(getAndOperate)的原子性,但是对于volatile变量的读/写,即单个操作是具有原子性的。
不要将volatile用在getAndOperate场合(这种场合不原子,需要再加锁),仅仅set或者get的场景是适合volatile的。
那么问题来了:下列哪些算是单操作,哪些算是复合操作?
x = 10; //语句1
y = x; //语句2
x++; //语句3
x = x + 1; //语句4
只有语句1是单操作(JVM会保证原子性,要么赋值成功,要么不成功),其他三个都是复合操作,即先读取x的值,+1操作,最后赋值。对于64位long/double型变量的读/写,从JSR-133内存模型开始(即从JDK5开始)仅仅只允许把一个64位long/double型变量的写操作拆分为两个32位的写操作来执行,但任意读操作必须要在单个读事物中执行。(来自java并发编程艺术)
volatile关键字的两层写读语义
volatile 写语义:当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量值刷新到主内存。即在写volatile变量之前,会保证所有之前的事已经发生,并且任何更新过的共享变量也是可见的(这句话也可以这样理解,保证volatile写之前的操作不会重排序到写之后)。
volatile读语义:当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效,线程会从主内存中读取共享变量。(这句话应该怎么理解捏?)
我是这样理解的:在读取volatile变量之前,本地内存的值无效(如果有多个处理器,其他处理器的缓存值无效),这意味着读取volatile变量以后的读取都在主内存进行.如果变量x没有volatile修饰,有可能变量x和后面的操作重排序,这样的话就保证不了x的可见性。
下面通过代码理解一下voliate特性:
public class VolatileFeatureExample {
volatile long v1 = 0L;
/**
* 单个变量的写
* 等价于 public void synchronized set(long)
* @param l
*/
public void set(long l){
v1=l;
}
/**
* 复合操作
* 等价于:
* long temp =get();
* temp+=1L;
* set(temp);
*/
public void getAndIncrement(){//复合操作 等价于
v1++;
}
/**
* 单个变量的写
* 等价于 public long synchronized get(long)
* @return
*/
public long get(){
return v1;
}
}
由上面代码可以看出,voliate单个操作相当于synchronized,但是复合操作,仅仅只能保证写的时候(set(temp))刷新到主内存的值是最新的,但是不能保证这两行代码是原子操作的,这就解释了为什么volalite在复合操作上没有原子性。如果大伙觉得这边我讲得不明白,木有事~~~~~下面经典的代码等着哈~long temp =get();
temp+=1L;
volatile保证原子性吗?
如果上面的分析看懂了,这个小节就可以略过了~~
请看下面例子:
//inc volatile变量
public volatile int inc = 0;
//自增操作
public void increase() {
inc++;
}
public static void main(String[] args) {
final JMM test = new JMM();
//10个线程,每个线程对inc自增1000次
for(int i=0;i<10;i++){
new Thread(){
public void run() {
for(int j=0;j<1000;j++) {
test.increase();
}
};
}.start();
}
System.out.println(test.inc);
}
output:9000
分析:线程A读取i的值到本地内存之后,还没来得及+1操作(因为volatile保证原子性操作),线程B读取i的值到本地内存,然后A与B线程各自对i+1,同时写入主内存,那么主内存的值为2,而不是3.将自增操作代码增加synchronized修饰即可保证原子性:
public synchronized void increase() {
inc++;
}
后记
自己也是最近刚看的书,综合书本和几个blog,总结了一下看法,献丑~~~如果有错误,麻烦指正一下哈~~现在的问题的是:有些锁的源码用到了volatile,为什么这些锁能够保证原子性~~各位等着哈,等我学习完,也会写出来了(捂脸~~)