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
@AllArgsConstructor
static 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线程没有修改成功