一 CAS作用介绍
1.1 CAS的概念
CAS的全程compare-and-swap,它是一条cpu并发原语,它的功能是判断内存某个位置是否为预期值,如果是则更改为新的值,这个过程是原子的。
cas是jdk提供的非阻塞原子操作,它通过硬件保证比较-更新的原子性。它是一条cpu的原子指令,不会造成所谓的数据不一致问题。cas的原子性实际上是cpu实现的独占,比起synchronized重量级锁,这里的排它要更短,所以多线程情况下性能就会比较好。
AtomicInteger类主要利用CAS+voltile和native方法来保证原子操作,从而避免synchronized的高开销,执行效率大为提升。
1.2 CAS作用
CAS有3个操作数,位置内存值V,旧的预期值A,要修改的更新值B,如果内存值V和预期值相同则,内存值改为B,否则什么都不做。当它重来重试的这种行为称为-自旋。
CAS是一条cpu的原子指令,不会造成所谓的数据不一致问题。CAS是靠硬件实现的从而在硬件层面提升效率,最底层还是交给硬件来保证原子性和可见性。
1.3.案例代码
1.4 unsafe类
在java中CAS操作的执行依赖于Unsafe类的方法,其中Unsafe类存在于sun.misc包中,其内部方法操作可以像C的指针一样直接操作内存。
注意unsafe类中的所有方法都是native修饰的,也就是说unsafe类中的方法都直接调用操作系统底层资源,执行相应任务。
AtomicInteger类主要利用CAS+voltile和native方法来保证原子操作,从而避免synchronized的高开销,执行效率大为提升。
CAS的全程compare-and-swap,它是一条cpu并发原语,它的功能是判断内存某个位置是否为预期值,如果是则更改为新的值,这个过程是原子的。
1.5 cas的缺点问题
1.循环时间开销大
2.ABA问题
举例:线程x从内存位置V中取出A,这个时候另一个线程y也从内从中取出A,并且线程y进行了一些操作将值变成了B,然后线程y又将V位置的数据变成A,这个时候线程x进行CAS操作发现内存中仍然是A,预期ok,然后线程x操作成功。
1.6 手写自旋锁案例代码
package com.ljf.thread.cas;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
/**
* @ClassName: CasDemo
* @Description: TODO
* @Author: admin
* @Date: 2023/12/16 00:04:06
* @Version: V1.0
**/
public class CasDemo {
AtomicReference<Thread> atomicReference=new AtomicReference<>();
public static void main(String[] args) {
CasDemo casDemo=new CasDemo();
new Thread(()->{
casDemo.lock();
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
casDemo.unlock();
},"A").start();
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
casDemo.lock();
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
casDemo.unlock();
},"B").start();
}
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....");
}
}
结果:
二 自旋锁
2.1 自旋锁的概念
CAS是实现自旋锁的基础,CAS利用CPU指令保证了操作的原子性,以达到锁的效果,至于自旋呢,就是尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁。当线程发现锁被占用时,会不断循环判断锁的状态,直到获取锁。这样做的好处是减少线程上下文切换的消耗。缺点是循环会消耗cpu。
解决办法是使用:版本号或者时间戳
2.2 案例ABA问题demo
package com.ljf.thread.cas;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicStampedReference;
/**
* @ClassName: Zixuan
* @Description: TODO
* @Author: admin
* @Date: 2023/12/15 20:55:32
* @Version: V1.0
**/
public class Zixuan {
static AtomicInteger atomicInteger=new AtomicInteger(100);
static AtomicStampedReference<Integer> atomicStampedReference=new AtomicStampedReference<Integer>(100,1);
public static void main(String[] args) {
new Thread(()->{
int stamp=atomicStampedReference.getStamp();
System.out.println(Thread.currentThread().getName()+"\t"+"首次版本号:"+stamp);
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
atomicStampedReference.compareAndSet(100,101,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);
System.out.println(Thread.currentThread().getName()+"\t 2次流水号:"+atomicStampedReference.getStamp());
atomicStampedReference.compareAndSet(101,100,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);
System.out.println(Thread.currentThread().getName()+"\t 3次流水号:"+atomicStampedReference.getStamp());
}).start();
new Thread(()->{
int stamp=atomicStampedReference.getStamp();
System.out.println(Thread.currentThread().getName()+"\t"+"首次版本号:"+stamp);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
boolean b=atomicStampedReference.compareAndSet(100,2002,stamp,stamp+1);
System.out.println(b+" "+atomicStampedReference.getReference()+"\t :"+atomicStampedReference.getStamp());
},"t4").start();
}
public static void abaCompare(){
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(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(atomicInteger.compareAndSet(100,2022)+"\t"+atomicInteger.get());
},"t2").start();
}
}
结果: