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

来源:互联网 发布:淘宝退款怎么退一部分 编辑:程序博客网 时间:2024/05/20 06:50

参考文章: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



阅读全文
0 0
原创粉丝点击