专注Java教育14年 全国咨询/投诉热线:444-1124-454
赢咖4LOGO图
始于2009,口口相传的Java黄埔军校
首页 hot资讯 一文读懂cas无锁机制

一文读懂cas无锁机制

更新时间:2022-12-27 11:49:32 来源:赢咖4 浏览680次

cas无锁机制是什么?赢咖4小编来告诉大家。cas无锁机制:CAS:Compare and Swap,即比较再交换。

1.Java内存模型:JMM(Java Memory Model)

内存模型当中定义了一个主内存,所有声明的实例变量都存在于主内存当中,主内存的数据会共享给所有线程,每一个线程有一块工作内存,工作内存当中主内存数据的副本

当更新数据时,会将工作内存中的数据同步到主内存当中

2.CAS无锁机制:

本身无锁,采用乐观锁的思想,在数据操作时对比数据是否一致,如果一致代表之前没有线程操作该数据,那么就会更新数据,如果不一致代表有县城更新则重试

CAS当中包含三个参数CAS(V,E,N),V标识要更新的变量,E标识预期值,N标识新值

运行过程:

(1)线程访问时,先会将主内存中的数据同步到线程的工作内存当中

(2)假设线程A和线程B都有对数据进行更改,那么假如线程A先获取到执行权限

(3)线程A先会对比工作内存当中的数据和主内存当中的数据是否一致,如果一致(V==E)则进行更新,不一致则刷新数据,重新循环判断

(4)这时更新完毕后,线程B也要进行数据更新,主内存数据和工作内存数据做对比,如果一致则进行更新,不一致则将主内存数据重新更新到工作内存,然后循环再次对比两个内存中的数据,直到一致为止。

CAS无锁机制存在一个问题

ABA问题,如果将原来A的值改为了B,然后又改回了A,虽然最终结果没有发生改变,但是在过程中是对该数据进行了修改操作。

解决该问题:在Java中并发包下有一个原子类:AtomicStampedReference,在该类当中通过版本控制判断值到底是否被修改。

解释:如果对值进行了更改则版本号+1,那么在CAS当中不仅仅对比变量的值,还要对比版本号,如果值和版本号都相等则代表没有被修改,如果有一方不相等代表进行过更改。

那么就从主内存中重新刷新数据到工作内存然后循环对比,直到成功为。

保证线程安全的三个方面:

(1)原子性:保证同一时刻该资源只能有一个线程访问修改,其他线程阻塞等待,例如Atomic包,锁

(2)可见性:一个线程对于主内存的数据操作对于其他线程是可见的

(3)有序性:一个线程观察其他线程中指令执行顺序,由于指令重排序存在,观察结果一般杂乱无序

原子性: 互斥访问,Atomic包,CAS算法,Synchronized,Lock

可见性:synchronized,volatile

顺序性:happends-before

原子类

public class AtomicTest {
//定义一个原子类对象
private AtomicInteger atomicInteger=new AtomicInteger();
public void getCount(){
//+1再返回
System.out.println(atomicInteger.incrementAndGet());
}
public static void main(String[] args) {
AtomicTest test=new AtomicTest();
for (int i = 1; i <=2; i++) {
new Thread(()->{
for (int j = 1; j <=100; j++) {
test.getCount();
}
}).start();
}
}
}

(4)AQS,即AbstractQueuedSynchronizer, 队列同步器,它是Java并发用来构建锁和其他同步组件的基础框架。来看下同步组件对AQS的使用:

AQS是一个抽象类,主是是以继承的方式使用。AQS本身是没有实现任何同步接口的,它仅仅只是定义了同步状态的获取和释放的方法来供自定义的同步组件的使用。从图中可以看出,在java的同步组件中,AQS的子类(Sync等)一般是同步组件的静态内部类,即通过组合的方式使用。

抽象的队列式的同步器,AQS定义了一套多线程访问共享资源的同步器框架,许多同步类实现都依赖于它,如常用的ReentrantLock/Semaphore/CountDownLatch

它维护了一个volatile int state(代表共享资源)和一个FIFO(双向队列)线程等待队列(多线程争用资源被阻塞时会进入此队列)

底层三个内容:

(1)state(用于计数器)

(2)线程标记(哪一个线程加的锁)

(3)阻塞队列(用于存放阻塞线程)

接下来以ReentrantLock的源码入手来深入理解下AQS的实现。

上面说过AQS一般是以继承的方式被使用,同步组件内部组合一个继承了AQS的子类。

在ReentrantLock类中,有一个Sync成员变量,即是继承了AQS的子类,源码如下:

 public class ReentrantLock implements Lock, java.io.Serializable {
    private static final long serialVersionUID = 7373984872572414699L;
    /** Synchronizer providing all implementation mechanics */
    private final Sync sync;
    /**
     * Base of synchronization control for this lock. Subclassed
     * into fair and nonfair versions below. Uses AQS state to
     * represent the number of holds on the lock.
     */
    abstract static class Sync extends AbstractQueuedSynchronizer {
        ...
    }
}

 

提交申请后,顾问老师会电话与您沟通安排学习

免费课程推荐 >>
技术文档推荐 >>