09-Java多线程-1、AQS-简介

AQS

一、简介

  • AQS,AbstractQueuedSynchronizer ,即队列同步器。它是构建锁或者其他同步组件的基础框架(如ReentrantLock、ReentrantReadWriteLock、Semaphore 等),J.U.C 并发包的作者(DougLea)期望它能够成为实现大部分同步需求的基础。它是JUC并发包中的核心基础组件。

二、优势

2.1 屏蔽细节

  • AQS 解决了在实现同步器时涉及当的大量细节问题,例如获取同步状态、FIFO同步队列。基于AQS来构建同步器可以带来很多好处。它不仅能够极大地减少实现工作,而且也不必处理在多个位置上发生的竞争问题。

2.2 提高性能

  • 在基于AQS 构建的同步器中,只能在一个时刻发生阻塞,从而降低上下文切换的开销,提高了吞吐量。同时在设计AQS时充分考虑了可伸缩性,因此JUC中,所有基于AQS构建的同步器均可以获得这个优势。在AQS中使用了模板模式,通过基础AQS实现特定的方法,可以方便的实现自定义的同步器,如锁等。

2.3 AQS体系

三、AQS源码初探

3.1 同步状态

  • AQS使用一个int类型的成员变量state来表示同步状态,在处理线程同步的过程中,各个线程本质是就是对这个state来进行读写操作,根据操作的结果决定线程是执行还是进入排队队列,具体细节暂不展开,先了解state。
  当 state > 0 时,表示已经获取了锁。(对于可重入锁state表示重入次数)
  当 state = 0 时,表示释放了锁。

3.2 同步队列

  • AQS通过内置的FIFO同步队列来完成资源获取线程的排队工作。如果当前线程获取同步状态失败(锁)时,AQS则会将当前线程以及等待状态等信息构造成一个节点(Node)并将其加入同步队列,同时会阻塞当前线程。当同步状态释放时,则会把节点中的线程唤醒,使其再次尝试获取同步状态。

3.3 主要方法

  • AQS 主要提供了如下方法:
3.3.1 同步状态的操作方法
  • getState():返回同步状态的当前值。
  • setState(int newState):设置当前同步状态。
  • compareAndSetState(int expect, int update):使用 CAS设置当前状态,该方法能够保证状态设置的原子性。
3.3.2 子类重写方法
3.3.2.1 独占式
  • 【可重写】tryAcquire(int arg):独占式获取同步状态,获取同步状态成功后,其他线程需要等待该线程释放同步状态才能获取同步状态。
  • 【可重写】tryRelease(int arg):独占式释放同步状态。
3.3.2.2 共享式
  • 【可重写】tryAcquireShared(int arg):共享式获取同步状态,返回值大于等于0,则表示获取成功;否则,获取失败。
  • 【可重写】tryReleaseShared(int arg):共享式释放同步状态。
  • 【可重写】isHeldExclusively():当前同步器是否在独占式模式下被线程占用,一般该方法表示是否被当前线程所独占。
3.3.2.3 源码
  • 可重写的方法,在AQS中都没有实现,都是抛出一个异常,交给子类实现,如下:
    protected boolean tryAcquire(int arg) {
        throw new UnsupportedOperationException();
    }
3.3.3 模板方法
3.3.3.1 独占式
  • acquire(int arg):独占式获取同步状态。如果当前线程获取同步状态成功,则由该方法返回;否则,将会进入同步队列等待。该方法将会调用可重写的 #tryAcquire(int arg) 方法;
  • acquireInterruptibly(int arg):与acquire(int arg) 相同,但是该方法可以响应中断。当前线程为获取到同步状态而进入到同步队列中,如果当前线程被中断,
    则该方法会抛出InterruptedException 异常并返回。
  • tryAcquireNanos(int arg, long nanos):超时获取同步状态。如果当前线程在nanos时间内没有获取到同步状态,那么将会返回false,已经获取则返回true 。
  • release(int arg):独占式释放同步状态。该方法会在释放同步状态之后,将同步队列中第一个节点包含的线程唤醒。
3.3.3.2 共享式
  • acquireShared(int arg):共享式获取同步状态。如果当前线程未获取到同步状态,将会进入同步队列等待,与独占式的主要区别是在同一时刻可以有多个线程获取到同步状态;
  • acquireSharedInterruptibly(int arg):共享式获取同步状态,可以响应中断。
  • tryAcquireSharedNanos(int arg, long nanosTimeout):共享式获取同步状态,增加超时限制。
  • releaseShared(int arg):共享式释放同步状态。
3.3.3.3 源码
  • 在模板方法中都会调用子类重写方法,因为模板方法是将已经固定的方法调用流程固定下来,只将逻辑实现不同的部分交给子类重写。如下是acquire(int arg)方法的源码,
    我们可以看到,他是基于tryAcquire(arg)来实现的,而tryAcquire(arg)就是交给子类重写的方法,在acquireQueued里面我们也可以看到tryAcquire(arg)的身影。因为整个逻
    辑流程可以在AQS中固定下来,只有tryAcquire(arg)的部分可能因为子类的不同而实现不同,这是典型的模板模式的应用。
    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
    
    final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

四、小结

4.1 方法结构

  • 从上面的方法看下来,基本上可以分成 3 类:
1. 交给子类重写的方法。在3.4.1和3.4.2中的方法,这部分方法主要是给子类进行重写,来实现不同的锁功能,
在不同的锁或者同步工具类中,这几个方法的实现逻辑可能会不一样,因此AQS将该部分方法交给子类重写。
2. 模板方法:模板方法是AQS中实现逻辑的核心方法,在模板方法中会调用交给子类重写的方法来完成指定的逻
辑,这部分方法的逻辑已经固定的,只是把流程不一样的地方抽象出来给子类重写。
3. 查询同步队列中的等待线程情况,这部分方法重写不多
  • 交给子类重写的方法和模板方法都分为独占式和共享式,方法基本是是对称分布的,通过方法名称很好分别。

  • 如下:

[外链图片转存失败(img-gOSYKFw5-1565596588556)(https://note.youdao.com/yws/api/personal/file/CF5EC1825FB944F484114543F4E77C55?method=download&shareKey=bf32d14b1599c2a970d4cb26853eb3cd)]

  • 子类重写方法所示如下:
方法作用独占式共享式
获取同步状态tryAcquire(int arg)tryAcquireShared(int arg)
释放同步状态tryRelease(int arg)tryReleaseShared(int arg)
判断是否被当前线程所独占isHeldExclusively()
  • 模板方法所示如下:
方法作用独占式共享式
获取同步状态acquire(int arg)acquireShared(int arg)
获取同步状态(可响应中断)acquireInterruptibly(int arg)acquireSharedInterruptibly(int arg)
获取同步状态(可超时)tryAcquireNanos(int arg, long nanos)tryAcquireSharedNanos(int arg, long nanosTimeout)
释放同步状态release(int arg)releaseShared(int arg)

4.2 AQS小结

  • 本文我们主要是对AQS做一个初步的认识,AQS在整个并发体系中非常重要,它是很多显示锁和同步工具的实现基础。

  • 在AQS中定义了通用的线程之间的同步等待逻辑,使用了模板模式来让子类的实现变得尽量简单,其内部使用FIFO的队列来完成线程之间的同步等待工
    作,子类继承AQS来实现不同场景下的功能。

  • 关于AQS更加详细的分析在后续的文章展开。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值