CAS
原子类引入
加入原子整型类的操作后,无锁化的操作
CAS 比较并交换
硬件级别的保证
源码分析
引出UnSafe类
UnSafe源码分析
unsafe中的do-while保证自旋
public final int getAndAddInt(Object var1, long var2, int var4) {int var5;do {var5 = this.getIntVolatile(var1, var2);} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));return var5;}
CAS是一条CPU的原子指令,不会造成所谓的数据不一致问题
native修饰的方法代表的是底层的方法
CAS是靠硬件实现的,从而在硬件层面提升效率。最底层还是交给硬件来保证原子性和可见性。实现方式是基于硬件平台的汇编指令,在intel的CPU中(X86机器上),使用的是汇编指令cmpxchg指令。
核心思想就是:比较要更新变量的值V和预期值E(compare),相等才会将V的值设为新值N(swap),如果不相等自旋再来。
原子引用
public class TestAutomicReference {@ToString@Data@AllArgsConstructorstatic class Use{private String name;private int age;}public static void main(String[] args) {AtomicReference<Use> useAtomicReference = new AtomicReference<>();Use lisi = new Use("lisi", 10);useAtomicReference.set(lisi);Use zhangsan = new Use("zhangsan", 12);boolean b = useAtomicReference.compareAndSet(lisi, zhangsan);System.out.println("result:"+b+"\t\t"+useAtomicReference.get());}
}
CAS与自旋锁,借鉴CAS思想
自旋锁
手写自旋锁demo
package com.aiguigu.juclearn.atomic;/*** @author: Runqiang_Jiang* @Time: 2024/3/16 11:55*/import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;/*** 题目:实现一个自旋锁* 自旋锁的好处:循环比较获取,没有类似wait的阻塞* 通过CAS操作完成自旋锁,A线程先进来调用myLock方法自己持有锁5秒钟,* B随后进来后发现当前有线程持有锁,所以只能通过自旋等待,知道啥A释放锁后B随后抢到*/
public class SpinLockDemo {AtomicReference<Thread> atomicReference=new AtomicReference<>();public void lock(){Thread thread = Thread.currentThread();System.out.println(Thread.currentThread().getName()+"\t"+"------come in");while (!atomicReference.compareAndSet(null, thread)) {}}public void unlock(){Thread thread = Thread.currentThread();atomicReference.compareAndSet(thread,null);System.out.println(Thread.currentThread().getName()+"\t"+"-----task over unlock");}public static void main(String[] args) throws InterruptedException {SpinLockDemo spinLockDemo = new SpinLockDemo();new Thread(()->{spinLockDemo.lock();//暂停几秒钟try {TimeUnit.SECONDS.sleep(5);} catch (InterruptedException e) {e.printStackTrace();}spinLockDemo.unlock();},"A").start();//暂停500毫秒,线程A先于线程B启动TimeUnit.MILLISECONDS.sleep(500);new Thread(()->{spinLockDemo.lock();spinLockDemo.unlock();},"B").start();}}
CAS的两大缺点
1.循环时间长开销很大
2.引出来ABA问题???
解决方案:版本号时间戳原子引用
demo演示
package com.aiguigu.juclearn.atomic;import java.util.concurrent.atomic.AtomicStampedReference;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;/*** @author: Runqiang_Jiang* @Time: 2024/3/16 12:18*/@AllArgsConstructor
@NoArgsConstructor
@Data
class Book{private int id;private String bookName;
}public class AtomicStampedDemo {public static void main(String[] args) {Book java = new Book(1, "java");AtomicStampedReference<Book> stampedReference=new AtomicStampedReference<>(java,1);System.out.println(stampedReference.getReference()+"\t"+stampedReference.getStamp());Book mysql = new Book(2, "mysql");boolean b ;b= stampedReference.compareAndSet(java, mysql, stampedReference.getStamp(),stampedReference.getStamp() + 1);System.out.println(b+"\t"+stampedReference.getReference()+"\t"+stampedReference.getStamp());b= stampedReference.compareAndSet(mysql, java, stampedReference.getStamp(),stampedReference.getStamp() + 1);System.out.println(b+"\t"+stampedReference.getReference()+"\t"+stampedReference.getStamp());}
}
ABA后版本号有变化
ABA多线程demo
package com.aiguigu.juclearn.atomic;import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicStampedReference;/*** @author: Runqiang_Jiang* @Time: 2024/3/16 12:31*/public class ABADemo {static AtomicInteger atomicInteger = new AtomicInteger(100);static AtomicStampedReference<Integer> stampedReference = new AtomicStampedReference<>(100,1);public static void main(String[] args) {new Thread(()->{int stamp = stampedReference.getStamp();System.out.println(Thread.currentThread().getName()+"\t"+"首次版本号:"+stamp);//休眠保证后面的t4线程拿到的版本号与t3一样try {TimeUnit.MILLISECONDS.sleep(500);} catch (InterruptedException e) {}stampedReference.compareAndSet(100,101,stampedReference.getStamp(),stampedReference.getStamp()+1);System.out.println(Thread.currentThread().getName()+"\t"+"2次版本号:"+stampedReference.getStamp());stampedReference.compareAndSet(101,100,stampedReference.getStamp(),stampedReference.getStamp()+1);System.out.println(Thread.currentThread().getName()+"\t"+"3次版本号:"+stampedReference.getStamp());},"t3").start();new Thread(()->{int stamp = stampedReference.getStamp();System.out.println(Thread.currentThread().getName()+"\t"+"首次版本号:"+stamp);//等待t3线程发生了ABA的问题try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}boolean b = stampedReference.compareAndSet(100, 2024, stamp, stamp + 1);System.out.println(b+"\t"+Thread.currentThread().getName()+"\t"+stampedReference.getReference()+"\t"+stampedReference.getStamp());},"t4").start();}private static void abahappens() {new Thread(()->{atomicInteger.compareAndSet(100,101);try {TimeUnit.MILLISECONDS.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}atomicInteger.compareAndSet(101,100);},"t1").start();new Thread(()->{try {TimeUnit.MILLISECONDS.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}boolean b = atomicInteger.compareAndSet(100, 2024);System.out.println(b+"\t"+atomicInteger.get());},"t2").start();}}
版本号不一致,t4线程没有修改成功