乐观锁--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操作无法分辨,值是否已经发生过变化):解决方法->在修改值的同时增加一个时间戳,只有当预期值和时间戳都相同时才进行修改。