您的位置:首页 > 其它

atomicity and volatility 代替synchronized 对象锁 的另一种方法(原子操作+可见性)

2017-06-21 15:34 417 查看
参考文章: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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  atomicity and volati