zoukankan      html  css  js  c++  java
  • cas无锁机制

    cas无锁机制:CAS:Compare and Swap,即比较再交换。
    1 .1Java内存模型:JMM(Java Memory Model)
    在内存模型当中定义了一个主内存,所有声明的实例变量都存在于主内存当中,主内存的数据会共享给所有线程,每一个线程有一块工作内存,工作内存当中主内存数据的副本
    当更新数据时,会将工作内存中的数据同步到主内存当中

    1.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当中不仅仅对比变量的值,还要对比版本号,如果值和版本号都相等则代表没有被修改,如果有一方不相等代表进行过更改
    那么就从主内存中重新刷新数据到工作内存然后循环对比,直到成功为止~


    2.保证线程安全的三个方面:
    1.原子性:保证同一时刻该资源只能有一个线程访问修改,其他线程阻塞等待,例如Atomic包,锁
    2.可见性:一个线程对于主内存的数据操作对于其他线程是可见的
    3.有序性:一个线程观察其他线程中指令执行顺序,由于指令重排序存在,观察结果一般杂乱无序
    原子性: 互斥访问,Atomic包,CAS算法,Synchronized,Lock

    可见性:synchronized,volatile

    顺序性:happends-before



    3.原子类

    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 {
            ...
        }
    }
  • 相关阅读:
    好文推荐 | 走近NLP学术界
    好文推荐 | 自然语言处理简介
    白嫖SSL证书部署(结合宝塔)
    基于本体体系的知识图谱构建
    Apache Jena Fuseki使用
    Springboot2.x入门——helloWorld
    Markdown语法
    freeswitch:error C2220: 警告被视为错误
    yate: windows下源码下载,配置,编译
    Android开源框架:volley
  • 原文地址:https://www.cnblogs.com/danxun/p/12575269.html
Copyright © 2011-2022 走看看