乐观锁--CAS(Compare and swap)算法以及ABA问题

1.首先做一个测试:i++。输出结果为10,因为在底层实现的时候会引入一个临时变量具体为:

 1 public static void main(String[] args) {
 2         //i++测试:
 3         int i=10;
 4         i=i++;
 5         System.out.println(i);
 6         /*
 7         *底层原理:
 8         _temp = i ;
 9         i = i + 1 ;
10         i = _temp ;
11         * */
12     }

所以i++就是一个非原子性操作,采用多线程再次测试:测试结果中会因为i++的非原子性操作带来读写不一致问题。比如:1,3,2,2,4,5,6,7,8,9序列的出现。两个2的出现就说明了,两个线程在同时从主存中读取,和进行加一操作时出现的不一致问题,那么可以用Volatile来解决这个不一致问题吗?不行。因为,这是一个非原子性操作。

volatile变量具有 有序性(防止指令重排序),可见性(对数据的写操作直接写到内存中),不具有原子性。

 1 public class TestAtomicDemo {
 2     public static void main(String[] args) {
 3 
 4         Testi testi = new Testi();
 5 
 6         for (int i = 0; i < 10; i++) {
 7             new Thread(testi, String.valueOf(i)).start();
 8         }
 9     }
10 }
11 class Testi implements Runnable{
12     int a=1;
13     @Override
14     public void run() {
15         try {
16             Thread.sleep(1000);
17         } catch (InterruptedException e) {
18             e.printStackTrace();
19         }
20         System.out.println(Thread.currentThread().getName()+": "+getA());
21     }
22     public int getA() {
23         return a++;    
24     }
25 }

2.Java并发包(java.util.concurrent)的主要实现机制:

采用Volatile保证内存的可见性;

采用CAS(Compare And Swap)算法保证数据的原子性。

 

3.乐观锁(CAS算法)和悲观锁(synchronized):

Synchronized就是一种悲观锁,因为它总是假设最坏的情况,每次去拿数据都会认为别人会修该,所以每次都会加锁,效率较低。

乐观锁其实是一种思想,它具体有两个步骤:冲突检测和数据更新。所以它不会每次访问都进行加锁控制,只是在进行更新操作时才进行冲突检测和数据更新,适合用于读操作较多的程序,因此大大提高了吞吐量。其实现方式比较典型的就是CAS算法。当多个线程同时更新同一个变量时,只会有一个线程成功修改,其他的都会失败,更新失败的线程不会被挂起,而是被告知在这次竞争中失败了,并可以再次尝试。

4.CAS(Compare And Swap)原理: 

首先涉及三个操作数:

内存值(V)--->首次读取内存中的值;

预估值(A)--->再一次读取的内存中的值;

更新值(B)--->新值;

每次在进行更新操作时,当且仅当V==A(内存值和预期值相等时),后 V=B(将内存值更新为B),否则不进行任何操作。

CAS是一种非阻塞算法的常见实现,JUC性能因此大大提升。

 1 //模拟CAS算法:
 2 public class TestCAS {
 3     public static void main(String[] args) {
 4         Cas cas = new Cas(10);
 5         for (int i = 0; i < 10; i++) {
 6             new Thread(() -> {
 7                 //10个线程都进行对数据的更新操作
 8                 int expectedValue = cas.getValue();  //更新前获得预期值
 9                 boolean b = cas.CompareTest(expectedValue, (int) Math.random());
10                 System.out.println(b);
11 
12             }).start();
13         }
14     }
15 }
16 class Cas{
17     /*
18     * 内存值;预期值;新值。
19     * */
20 
21     int value;  //内存值
22 
23     public synchronized int getValue() {
24         return value;
25     }
26 
27     public Cas(int value) {   //构造方法
28         this.value = value;
29     }
30 
31     //比较交换,并将内存值返回
32     public int CompareSwap(int expectedValue,int newValue){
33         int oldValue=value;  //获得内存值
34         if(oldValue==expectedValue){
35             value=newValue;
36         }
37         return oldValue;
38     }
39 
40     //检测是否更新成功
41     public boolean CompareTest(int expectedValue,int newValue){
42         boolean flag=false;
43         if(expectedValue==CompareSwap(expectedValue,newValue)){
44             //判断预期值和内存值是否相等->更新是否成功
45             flag=true;
46         }
47         return flag;
48     }
49 }

CAS算法的ABA问题(当前线程的CAS操作无法分辨,值是否已经发生过变化):解决方法->在修改值的同时增加一个时间戳,只有当预期值和时间戳都相同时才进行修改。

 

 


posted @ 2019-06-17 16:38  德鲁大叔817  阅读(693)  评论(0编辑  收藏  举报