AQS详解


title: JUC.AQS

AQS提纲

AQS简介

AbstractQueuedSynchronizer抽象同步简称AQS,它是实现同步器的基础组件,并发包中的锁底层就是通过AQS实现。下面看下AQS类的UML类图

avatar
由该图可以看出来AQS是一个FIFO的双向队列,其内部通过Node节点head和tail记录队列的首尾元素。
AQS提供资源独占和共享两种方式,两种方式通过内部的state信息描述线程状态实现独占以及共享。
Node节点内部的SHARED用来标记当前线程获取共享资源被阻塞挂起放入AQS队列。
EXCLUSIVE用来标记线程获取独占资源被挂起放入AQS队列。
waitStatus记录当前线程的等待状态,可以为CANCELLED被取消,SIGNAL线程需要被唤醒,CONDITION线程在条件队列里面等待,PROPAGATE释放共享资源需要通知其它线程节点。

依赖工具类LockSupport

LockSupport是一个工具类,它的主要作用是挂起和唤醒线程,该工具类是创建锁和其它同步类的基础。
LockSupport类是使用Unsafe类实现的,主要方法有park与unpark(Thread thread),也提供定时的方法。
划重点啦,每个使用它的线程都会关联一个许可证,在默认情况下调用是不持有许可证的!
为什么说上面这一段因为当调用park方法的线程已经拿到了与LockSupport关联的许可证,则调用会马上返回不会阻塞。否则会被阻塞挂起,相比较CAS的阻塞要性能高一点。

获取与释放锁

  • 子类实现AQS方法
    1.tryAcquire(int arg)尝试获取独占锁
    2.tryRelease(int arg)释放独占锁
    3.tryAcquireShared(int arg) 尝试获取共享锁
    4.tryReleaseShared(int arg)释放共享锁
  • 独占方式获取锁
    当一个线程调用acquire(int arg)方法获取独占锁时,会先调用tryAcquire方法尝试获取资源设置state状态。
    成功则返回否则将线程封装成Node节点设置EXCLUSIVE状态插入到AQS队列的尾部,并调用LockSupport.park(this)挂起自己。
    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
  • 独占方式释放锁
    当一个线程调用release(int arg)方法会尝试使用tryRelease操作释放资源,并设置state状态,然后调用LockSupport.unpark(thread)方法激活AQS队列里面
    当前释放节点的next节点并尝试调用tryAcquire,查看当前状态变量的state是否满足,满足激活否则挂起放入队列。
    public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);//具体CAS修改状态,LockSupport.unpark(s.thread)唤醒next线程Node节点。
            return true;
        }
        return false;
    }
  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);
        }
    }

ConditionObject

notify与wait是配合synchronized内置锁实现线程间同步的基础设施一样,条件变量的signal和await方法也是用来配合锁实现线程间同步的基础设施。
它们的不同在于synchronized同时只能与一个共享变量实现同步,而AQS可以对应多个条件变量。
比如生产者和消费者使用ConditionObject可以有两个condition
并且每个ConditionObject都是一个队列存入的是线程Node节点,至于为什么这么做后续在写ReentrantLock或者实现自定义的一个非重入锁的时候写。
我觉得更加便于理解,这一章只是大概描述有这么一个东西。

欢迎扫码加入知识星球继续讨论
avatar

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值