参考文章:http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#volatile
https://www.ibm.com/developerworks/library/j-jtp06197/index.html
https://tutorials.jenkov.com/java-concurrency/volatile.html
1、atomicity(原子操作):一个操作不中断,一直执行完
对成员变量进行操作。
volatile:就是在原子操作下,保证变量的visible。
2、按照:http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#volatile
编译和运行时:
1>禁止分配它们到寄存器(registers),保证一个线程写后能马上更新到内存,所有线程都能看见,
2>必须保证volatile变量读之前缓冲区(cpu cache)无效,哪么线程只能在内存读。
class VolatileExample {
int x = 0;
volatile boolean v = false;
public void writer() {
x = 42;
v = true;
}
public void reader() {
if (v == true) {
//uses x - guaranteed to see 42.
}
}
}
1>如果v不是volatile变量,哪么reader()读操作,当v==true,有可能读到x=0。
原因:有可能编译器重新排序 v=true,x=42,哪么读出的是未改变的值。
所以:编译器看见有volatile变量存在的地方,有cpu缓存,但是读时不让去缓冲读,直接从内存读,不重新排序。
一、功能介绍
1、atomicity :原子操作,也叫lock-free 无锁,即不需要锁操作。
优点:操作不能被thread 调度者中断,一气呵成,比如:正在高考数学,考完才出来吧!
atomicity :对应单cpu ,在多cpu用 visibility代替。
2、比较
synchronized:不能保证共享变量前的所有变量数据(非共享数据)一定会存入内存。 如果是共享数据,哪么感觉两个线程基本上会看见新值。 因为这syn对象锁保证只有单独一个线程读数据后,保存到内存,下一个线程才能读,这是syn对象锁的基本功能。 | atomicity,volatile: 编译器或jvm不能重新排序volatil 指令的执行顺序。 |
特殊情况有可能如下: 一个synchronized 线程改变成员变量值后 1》首先应该写入到缓冲中,如果当前干活线程改变值后,值存放在缓中,未向内存写。 2》这时手动删除本window7的缓存, 3》下一个线程执行的话,去内存取,还是以前的值,所以刚刚改的值根本未入内存 感觉只有这种情况下才会出错,一般应该没问题。 | 1>一个线程写入变量后,马上入内存。 2>多个线程读,直接去内存读,所以这种情况数据不存在一致性问题。 但是,如果一个线程写入变量到cache后,未入内存,刚好另一个线程从内存读,又出现一致性问题了。 |
目前默认原子操作的变量:primtive types(int,char,byte):原子操作,读、写只需要一条指令即可。
1》返回值 return i;这是一个原子操作。
3、 volatility:对syn对象锁的补充。
a long or double:是64位,一次读,非原子操作,需要分成两个32位来操作,对应两条指令,两条指令中间可以插入其它指令。
(1) 设置成员变量:非volatility
当对一个线程对成员变量进行的an atomic 操作时,就我一个干活线程用,别的干活线程不用,哪么也不需要刷新main memory了(主要目的让别的线程看见新改的值)
(2)设置成员变量:volatility (有波动的意思)
1>实时更新数据
多个干活线程访问同一个成员变量时,必须设置成volatility ,当一个线程改了成员变量值,哪么所有用到这个成员变量的干活线程必须能看见新改的值。
2>volatility 可以让一个long 变量成为原子操作?未测试
本来一个64位的 long是非原子操作。
二、例子: 一看,感觉下面两个方法对成员变量i是原子操作
但是指令“get” 和 "put" 之间,另一个对象也可以修改成员变量的值,所以下面操作非原子。
Atomicity 类,可以new Atomicity(),生成多个对象,并且多个对象都可以操作f1(),f2()方法,有可能存在先执行,同时执行等待的可能。
package concurrency; public class Atomicity { int i; void f1() { i++; } void f2() { i += 3; } } |
void f1(); Code: 0: aload_0 1: dup 2: getfield #2; //取得成员变量i 5: iconst_1 6: iadd 7: putfield #2; //保存成员变量i 10: return void f2(); Code: 0: aload_0 1: dup 2: getfield #2; //Field i:I 5: iconst_3 6: iadd 7: putfield #2; //Field i:I 10: return |
三、不要盲目使用原子的思想
下面例子本想取一个偶数值,但是由于getValue 以为是原子操作,没有设置对象锁。
因此当evenIncrement 执行到一个i++后(奇数)------getValue 取值,出现错误。
1、成员变量 i
2、方法getValue(只有返回值),是一个原子操作
3、方法 evenIncrement 带有对象锁 自增1,两次,
4、干活线程AtomicityTest,
package concurrency; import java.util.concurrent.*; public class AtomicityTest implements Runnable { private int i = 0; public int getValue() { return i; } private synchronized void evenIncrement() { i++; i++; } public void run() { System.out.println("run 开始!"); while (true) evenIncrement(); } public static void main(String[] args) { ExecutorService exec = Executors.newCachedThreadPool(); AtomicityTest at = new AtomicityTest(); exec.execute(at); System.out.println("main!"); while (true) { System.out.println("main 循环开始!"); int val = at.getValue(); System.out.println("val ="+val); if (val % 2 != 0) { System.out.println(val); System.exit(0); } } } } |
参考文章:http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#volatile