currentPackage(CAS、AQS、CLH、LOCK、ReentrantLock、CountDownLatch等)

本文详细介绍了Java并发编程中的关键工具类,包括CAS无锁算法、AbstractQueuedSynchronizer(AQS)、ReentrantLock以及并发容器如CountDownLatch、Semaphore和CyclicBarrier的原理与应用。AQS是构建锁和同步组件的框架,利用CLH队列管理线程等待,支持独占和共享两种同步方式。ReentrantLock基于AQS,通过state字段实现可重入。此外,文章还涵盖了CountDownLatch用于线程等待,Semaphore用于并发线程数控制,CyclicBarrier用于线程同步点的设置。
摘要由CSDN通过智能技术生成

currentPackage

CAS(Compare and Swap)

CAS是什么?
  • 无锁算法。内存值V、旧的预期值A、要修改的新值B。当V==A,说明值没变,此时修改V为B,否则什么都不做。
CAS原理?
  • 主要是由sun.misc.Unsafe这个类通过JNI调用CPU底层指令实现。
有什么特性?
  • 是CPU指令级的操作,只有一步原子操作,所以非常快。
  • 避免了请求操作系统来裁定锁的问题,不用麻烦操作系统,直接在CPU内部就搞定。

AbstractQueuedSynchronizer(AQS)

AQS是什么?
  • 用于构建锁和同步容器的框架。
AQS核心思想?
  • 如果被请求的共享资源空闲:将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。(资源空闲,则想要的线程才有效,并将资源锁定)
  • 如果被请求的共享资源被占用:则需要一套针对线程阻塞等待以及被唤醒时的锁分配的机制该机制AQS是用CLH队列锁实现的,将暂时获取不到锁的线程加入到队列中。
AQS原理?
  • AQS使用一个FIFO的队列(CLH队列,是CLH锁的变形),表示排队等待锁的线程。
  • 队列头节点称作“哨兵节点”或者“哑节点”,它不与任何线程关联。
  • 其他节点与等待线程关联,每个节点维护一个等待状态waitStatus。
CLH(Craig,Landin,and Hagersten)队列是?
  • 分为两部分:

    1. Sync queue:同步队列,是一个双向列表。包括head节点和tail节点。head节点主要用作后续的调度。
    2. Condition queue:非必须,单向列表。当程序中存在cindition的时候才会存在此列表。
  • 是一个虚拟的双向队列(虚拟的双向队列即不存在队列实例,仅存在结点之间的关联关系)。

  • AQS是将每条请求共享资源的线程封装成一个CLH锁队列的一个结点(Node)来实现锁的分配。

节点

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MZeT2vxD-1641729469524)(/Users/gz/Library/Application Support/typora-user-images/image-20220109184658153.png)]

AQS实现?
  • AQS使用一个int成员变量来表示同步状态。使用CAS对该同步状态进行原子操作实现对其值的修改。

    /**
     * The synchronization state.
     */
    private volatile int state;
    
    //返回同步状态的当前值
    protected final int getState() {
           
            return state;
    }
    // 设置同步状态的值
    protected final void setState(int newState) {
          
            state = newState;
    }
    //原子地(CAS操作)将同步状态值设置为给定值 update,如果当前同步状态的值等于 expect(期望值)
    protected final boolean compareAndSetState(int expect, int update) {
         
            return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
    }
    
  • 通过内置的FIFO队列来完成获取资源线程的排队工作。

  • 大致实现思路:

    1. AQS内部维护了一个CLH队列来管理锁。

    2. 线程会首先尝试获取锁,如果失败就将当前线程及等待状态等信息包装成一个node节点加入到同步队列sync queue里。

      2.1 如果当前节点为head的直接后继,则会不断的循环尝试获取锁。

      2.2 如果失败就会阻塞自己直到自己被唤醒。而当持有锁的线程释放锁的时候,会唤醒队列中的后继线程。(失败就眯一会儿等着,前面的人使用完会顺延通知后面相邻的弟兄。

独占与共享?
  • 在使用层面看,AQS的功能主要分为两类:独占和共享

    1. 它的所有子类中,要么实现并使用了它的独占功能的api,要么使用了共享锁的功能,而不会同时使用两套api。
    2. 即便是最有名的子类ReentrantReadWriteLock也是通过两个内部类读锁和写锁分别实现了两套api来实现的。
  • AQS定义两种资源共享方式:Exclusive(独占,只有一个线程能执行,如ReentrantLock)和Share(共享,多个线程可同时执行,如Semaphore/CountDownLatch)。

  • 独占式同步组件来讲,同一时刻只有一个线程能获取到同步状态,其他线程都得去排队等待。对于共享式同步组件来讲,同一时刻可以有多个线程同时获取到同步状态。

  • 不同的自定义同步器争用共享资源的方式也不同。自定义同步器在实现时只需要实现共享资源state的获取与释放方式即可,至于具体线程等待队列的维护(如获取资源失败入队/唤醒出队等),AQS已经在顶层实现好了。

    isHeldExclusively():该线程是否正在独占资源。只有用到condition才需要去实现它。
    tryAcquire(int):独占方式。尝试获取资源,成功则返回true,失败则返回falsetryRelease(int):独占方式。尝试释放资源,成功则返回true,失败则返回falsetryAcquireShared(int):共享方式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。
    tryReleaseShared(int):共享方式。尝试释放资源,如果释放后允许唤醒后续等待结点返回true,否则返回falsepublic final void acquire(int arg) {
         
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
    
    // 具体资源的获取/释放方式交由自定义同步器去实现
    protected boolean tryAcquire(int arg) {
         
            throw new UnsupportedOperationException();
    }
    
    private Node addWaiter(Node mode) {
         
         //以给定模式构造结点。mode有两种:EXCLUSIVE(独占)和SHARED(共享)
         Node node = new Node(Thread.currentThread(), mode);
         
         //尝试快速方式直接放到队尾。
         Node pred = tail;
         if (pred != null) {
         
             node.prev = pred
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值