AQS 介绍及核心原理

AbstractQueuedSynchronizer- AQS

AQS是一个用于构建锁、同步器、协作工具类的工具类(框架)。有了AQS以后,更多的协作工具类都可以很方便得被写出来。一 句话总结:有了AQS ,构建线程协作类就容易多了。

使用Node实现FIFO队列,可以用于构建锁或者其他同步装置的基础框架,利用了 int 类型表示状态,使用方法是继承利用了模板方法

子类通过继承并通过实现它的方法管理其状态 { acquire和 release } 的方法操纵状态

可以同时实现排它锁和共享锁模式(独占、共享)

AQS 具体实现大致思路:AQS 内部维护了一个 CLH 队列管理锁,线程首先尝试获取锁,如果失败就将当前线程以及等待状态等信息包装成一个 Node 节点加入到同步队列,接着不断循环尝试获取锁,条件是当前节点为 Head 直接后继才会尝试,如果失败就会阻塞自己直到自己被唤醒,当持有锁的线程释放锁的时候会唤醒队列中的后继线程,Semaphore、ReentrantLock、CountDownLatch 内部有一个 Sync 类,Sync 类继承了 AQS

源码

AQS最核心的就是三大部分:

state

这里的 state 的具体含义,会根据具体实现类的不同而不同。比如在Semaphore里它表示”剩余的许可证的数量“,而在CountDownLatch里,它表示"还需要倒数的数量”。state 是 volatile 修饰的,会被并发地修改,所以所有修改 state 的方法都需要保证线程安全,比如 getState、setState 以及compareAndSetState 操作来读取和更新这个状态。这些方法都依赖于J.U.C.Atomic 包的支持

/**
* The synchronization state.
*/
private volatile int state;

在 ReentrantLock中,state 用来表示"锁”的占有情况,包括可重入计数,当state的值为0的时候,标识改Lock不被任何线程所占有

控制线程抢锁和配合的FIFO队列

这个队列用来存放“等待的线程”,AQS就是"排队管理器”当多个线程争用同一把锁时,必须有排队机制将那些没能拿到锁的线程串在一起。当锁释放时,锁管理器就会挑选一个合适的线程来占有这个刚刚释放的锁

AQS 会维护一个等待的线程队列,把线程都放到这个队列里,这是一个双向链表的形式
在这里插入图片描述

期望协作工具类去实现的获取/释放等重要方法

这里的获取和释放方法,是利用AQS的协作工具类里最重要的方法,是由协作类自己去实现的,并且含义各不相同

获取方法:获取操作会依赖state变量,经常会阻塞(比如获取不到锁的时候),在Semaphore中获取就是acquire方法,作用是获取一个许可证,能不能获取到也取决于 state 变量,而在CountDownLatch里面获取就是await方法,作用是"等待“直到倒数结束

释放方法:释放操作不会阻塞,在 Semaphore 中,释放就是 release 方法,作用是释放一个许可证,CountDownLatch 里面,获取就是 countDown 方法作用是"倒数1个数“

用法

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

自己用 AQS 实现一个简单的 CountDownLatch

第一步:写一个类,想好协作的逻辑,实现获取/释放方法。

第二步:内部写一个Sync类继承 AbstractQueuedSynchronizer

第三步:根据是否独占来重写tryAcquire/tryRelease或tryAcquireShared (int acquires)和tryReleaseShared(intreleases)等方法,在之前写的获取/释放方法中调用AQS的acquire/release或者Shared方法

public class Test {

    private final Sync sync = new Sync();

    // 0 关闭 1 打开
    // 获取  共享所模式 利用 AQS
    public void await() { // 谁调用谁进入等待状态
        // if (tryAcquireShared(arg) < 0) 代表要排队阻塞
        sync.acquireShared(0); // 后去所调用 acquireShared 这里面的方法
    }

    public void signal() { // 之前阻塞的线程都会唤醒
        sync.releaseShared(0);
    }

    private class Sync extends AbstractQueuedSynchronizer {
        // tryAcquireShared 是 acquireShared 里面的,我们要自己实现
        @Override
        protected int tryAcquireShared(int arg) {
            // getState 这个值由 signal 来改变
            return (getState() == 1) ? 1 : -1;
        }

        // 返回 true 把之前所有线程唤醒
        @Override
        protected boolean tryReleaseShared(int arg) {
            setState(1);
            return true;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Test test = new Test();
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "尝试获取latch,获取失败等待");
                test.await();
                System.out.println("开闸放行" + Thread.currentThread().getName() + "继续运行");
            }).start();
        }

        Thread.sleep(5000);
        test.signal();

        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "尝试获取latch,获取失败等待");
            test.await();
            System.out.println("开闸放行" + Thread.currentThread().getName() + "继续运行");
        }).start();
    }
}

运行结果:

Thread-0尝试获取latch,获取失败等待
Thread-2尝试获取latch,获取失败等待
Thread-1尝试获取latch,获取失败等待
Thread-3尝试获取latch,获取失败等待
Thread-4尝试获取latch,获取失败等待
Thread-5尝试获取latch,获取失败等待
Thread-6尝试获取latch,获取失败等待
Thread-7尝试获取latch,获取失败等待
Thread-8尝试获取latch,获取失败等待
Thread-9尝试获取latch,获取失败等待
开闸放行Thread-0继续运行
开闸放行Thread-2继续运行
开闸放行Thread-1继续运行
开闸放行Thread-3继续运行
开闸放行Thread-4继续运行
开闸放行Thread-7继续运行
开闸放行Thread-5继续运行
开闸放行Thread-6继续运行
开闸放行Thread-9继续运行
开闸放行Thread-8继续运行
Thread-10尝试获取latch,获取失败等待
开闸放行Thread-10继续运行

Process finished with exit code 0

最后

回顾一下 AQS 的核心内容

  • state
  • 控制线程抢锁和配合的FIFO队列
  • 期望协作工具类去实现的获取/释放等重要方法
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值