AQS分析第二篇(其提供的受保护方法代表什么意思,如何使用AQS)

问题是最好的老师!

一、思考

第一篇分析了AQS是干什么的,其内部的数据结构是怎样的,第二篇依然不深入理解AQS的主要实现,先解决以下问题:

  • 如何使用AQS?

通过第一篇我们知道,AQS是一个同步框架,其提供了排队和阻塞等机制,子类需要实现其受保护方法,去自己定义state所代表的语义。那么其提供了哪些受保护方法,都是干嘛的,该如何使用,其提供的一般方法都是干嘛的,这一篇,我们将围绕这些问题进行分析。

 二、源码分析

  • 注释翻译

上一篇我们只翻译了注释的前半部分,注释的后半部分就是关于AQS使用介绍的,翻译如下:

要使用该类作为同步器的基础,需要重新定义下列方法,并在需要的时候通过getState,setState和compareAndSetState方法检索或者修改state的值。这些方法包括:

  1. tryAccquire
  2. tryRelease
  3. tryAcquireShared
  4. tryReleaseShared
  5. isHeldExclusively

该类中以上这些方法的默认实现都只是抛出UnsupportedOperationException。子类中对于这些方法的实现必须是内部线程安全的,而且一般情况下要简短,而且非阻塞。重写以上这些方式是使用AQS的唯一方式,其他方法被定义为final类型,不能独立的被更改。

您还可能会发现实现继承AQS的方法对于跟踪拥有独占同步器的线程非常有用,我们鼓励您去使用他们,这使监控和诊断工具能够帮助确定哪些线程持有锁。

即使该类内部是基于先进先出队列的,其也不强制先进先出的获取策略。以独占方式获取采用以下形式:

Acquire:
    while (!tryAcquire(arg)) {
       //如果该线程还未入队则将其入队 
       <em>enqueue thread if it is not already queued</em>;
       //如果可能阻塞当前线程
       <em>possibly block current thread</em>;
    } 
Release:
    if (tryRelease(arg))
        //唤醒对头线程
        <em>unblock the first queued thread</em>;

共享模式类似,但是可能涉及级联通知。

因为检查是否可获取是在入队之前进行的,所以一个新获取的线程可能会在其他被阻塞和排队的线程之前突然闯入。但是,如果需要你可以将tryAccquire和tryAccquireShared定义为通过内部调用一个或者多个检查方法类禁止突然闯入,从而提供公平的先进先出获取方式。

尤其是,大多数的公平获取的同步器可以定义为如果hasQueuedPredecessors(该方法是专门为公平同步器设置的)方法返回true则tryAccquire就返回fasle。其他的变种形式也是可以的。

吞吐量和可扩展性在默认的闯入策略(也称为greey,renouncement,convoy-avoidance)中是最高的。但这并不保证公平和无饥饿性。允许较早的排队线程在较晚的排队线程之前重新竞争,而且每次与即将进入的线程竞争都有公正的获得成功的机会。当获取没有进行自旋操作,在一般情况下,在阻塞之前,他们将会多次调用tryAccquire方法并同时加上一些其他的计算。当独占的同步器只是被短暂的持有时,自旋会带来最大的收益。当它不是独占形式时,不需要这种能力。如果需要你可以通过前面的调用来增强这一点,以获得带有fast-path检查的方法,可能需要预先检查{@link #hasContended}和/或{@link #hasQueuedThreads},只有在同步器可能不存在争用时才这样做。这个类提供了一个高效的、可伸缩的同步基础,部分原因是通过将它的使用范围专门化到可以依赖于{@code int}状态、获取和释放参数以及内部FIFO等待队列的同步器。当着不能满足你的需求时,你可以以更低水平的方式够造一个同步器,通过java.uti.concurrent.atomic包下的原子类,你自定义的java.util.Queue队列以及LockSupport的阻塞功能支持。

 

  •  受保护的方法

在注释翻译中我们知道AQS定义了几个受保护的方法,我们使用AQS的唯一方式就是去重新定义这几个方法。接下来,我们看看这几个方法的解释:

//尝试以独占的方式进行获取,该方法需要检查对象的state是否允许以独占模式被获取,如果允许则获取。

//该方法的调用时通过线程执行accquire方法,如果该方法报告失败,则accquire方法将当前线程入队阻塞。直到其他线程调用了release方法去通知它。其也可以被用来实现tryLock方法。

//该方法的默认实现抛出UnsupportedOperationException异常

//arg:获取参数,该值通常为1传递给accquire方法,或者是在进入条件等待时保存的值。或者,该值是未解释的
//表示你喜欢的任何内容。

//返回值:true代表成功获取

//抛出IllegalMointorException:如果accquire方法会将state设置为一个不合法的值。必须以一致的方式抛 
//出此异常,以便同步工作正常。
protected boolean tryAcquire(int arg) {
        throw new UnsupportedOperationException();
}

 

//试图以独占模式将state设置为其值可以反映锁已被释放。

//该方法会通过线程调用release方法进行调用

//默认实现抛出UnsupportedOperationException

//arg:释放参数。该值通常是1被传递给release方法,或者是在到达条件等待时,当时的值。在其他情况下,该值是未解释的,可以代表任何你喜欢的内容。

//返回值:true代表state已经完全被释放,其它等待的线程可以尝试获取。

protected boolean tryRelease(int arg) {
        throw new UnsupportedOperationException();
}

 

//尝试以共享模式进行获取。该方法需要查询当前对象的状态是否允许以共享方式进行获取,如果允许,则进行获 
//取

//该方法会通过线程调用accquireShared方法进行调用。如果该方法报告失败,如果其还没有入队,则accquireShared方法将该线程入队。直到其他线程调用了release方法通知该线程。

//arg:同tryAccquire方法

//返回值:一个负值代表失败。0代表当前线程以共享模式获取成功,但是没有后续的线程会通过共享模式模式
//成功获得。正直代表当前线程以共享模式获取成功,且后续的线程通过共享模式也可能获得成功。在这种情况下
//后续的等待线程必须检查是否可用性。(对三个不同返回值的支持使此方法能够用于仅在某些情况下才进行独占 
//操作的上下文中)。
protected int tryAcquireShared(int arg) {
        throw new UnsupportedOperationException();
}

 

//尝试将状态设置为可以反映当先线程已经释放锁的值。

//该方法通常会通过线程调用releaseShared方法进行调用。

//arg:释放参数。同tryRelease方法。

//返回值:true代表以共享模式释放允许之后以共享或者独占模式等待获取的操作可以成功。

protected boolean tryReleaseShared(int arg) {
        throw new UnsupportedOperationException();
}

 

//返回值:true代表同步器只被当前线程独占。此方法在每次调用非等待的方法时调用。

//该方法在AQS内部只有ConditonObject中的方法才会去调用它,所以如果不用condition的话,则不用重新定义该方法。

protected boolean isHeldExclusively() {
        throw new UnsupportedOperationException();
}

 

三、解答开篇 

  • 如何使用AQS?

答:通过上面的分析,我们可以重新定义AQS提供的受保护的方法并通过getState,setState,compareAndSetState方法改变state的值从而去定义自己的同步器的语义。总结来说:

  • 第一点:实现AQS的子类必须定义为内部类,作为外部类的同步辅助类。
  • 第二点:实现AQS受保护的方法包括:
  1. tryAccquire(int):表示已独占模式进行获取,详情请看上文讲解。
  2. tryRelease():表示已共享模式进行释放,详情请看上文讲解。
  3. tryAccquireShared(int):表示以共享模式进行获取,详情请看上文讲解。
  4. tryReleaseShared(int):表示以共享模式进行释放,详情请看上文讲解。
  5. isHeldExclusively():表示对象是否被当前线程独占。
  • 第三点:通过setSate(int),getState(),compreAndSetState(int)方法检索或者修改state的值。state代表的语义由自己控制。

该篇到此结束。

通过前两篇我们翻译了AQS的注释,了解了AQS的基本数据结构,AQS的使用场景,其提供的功能以及如何使用AQS,那么接下来我们将通过ReentrantLock的实现来具体了解AQS的使用方式以及其内部的实现原理。 

-------------------------------------------------------------------------------------------------------------------

知其然,更要知其所以然...

-------------------------------------------------------------------------------------------------------------------

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值