AQS的内部结构分析以及主要方法的讲解

  • AQS类

        队列同步器AbstractQueuedSynchronizer(简称AQS),是用来构建锁或者其他同步组件的基础框架,子类通过继承同步器并实现它的抽象方法来管理同步状态;它使用了一个int成员变量state表示同步状态,通过内置的FIFO队列来完成资源获取线程的排队工作(同步队列,等待队列)。

  • AQS中的主要方法

         访问或修改同步状态方法:

getState():获取当前同步状态。

setState(int newState):设置当前同步状态。

compareAndSetState(int expect,int update):使用CAS设置当前状态,该方法能够保证状态 设置的原子性。

同步器可重写的方法:

protected boolean tryAcquire(int arg);//独占式获取同步状态

protected boolean tryRelease(int arg);//独占式释放同步状态

protected int tryAcquireShared(int arg);//共享式获取同步状态

protected boolean tryReleaseShared(int arg);共享式释放同步状态

protected boolean isHeldExclusively();//判断线程是否独占式的持有同步状态

同步器提供的模板方法

同步器提供的模板方法基本上分为3类:独占式获取与释放同步状态、共享式获取与释放 同步状态和查询同步队列中的等待线程情况。自定义同步组件将使用同步器提供的模板方法 来实现自己的同步语义。

  • 同步队列:

同步器依赖内部的同步队列(一个FIFO双向队列)来完成同步状态的管理,当前线程获取 同步状态失败时,同步器会将当前线程以及等待状态等信息构造成为一个节点(Node)并将其 加入同步队列,同时会阻塞当前线程,当同步状态释放时,会把首节点中的线程唤醒,使其再 次尝试获取同步状态。 同步队列中的节点(Node)用来保存获取同步状态失败的线程引用、等待状态以及前驱和 后继节点

入队:插入节点是通过尾插的方式,其方法为compareAndSetTail(Node expect,Node update),两个参数分别是当前线程认为的尾节点,当前节点。设置成功后,当前节点才会与之前的尾节点建立联系。

出队:首节点是获取同步状态成功的节点,首节点的线程在释放同步状态时,将会唤醒后继节点,而后继节点将会在获取同步状态成功时将自己设置为首节点。

独占式同步状态的获取与释放:

获取流程(acquire()方法调用过程):

共享式同步状态的获取与释放

获取流程acquireShared

独占式超时获取同步状态(支持响应中断):doAcquireNanos(int arg,long nanosTimeout)

主要是对超时时间的处理,计算方法nanosTimeout-=now-last nanosTimeout;如果小于0则超时,如果大于0,则根据新的nanosTimeout继续等待,若nanosTimeout小于1000纳秒时,将不再使线程进入等待状态,而是进入到快速的自选,这个就是为了提高时间的精确度。

  • 重入锁

重入是指任意线程在获取到锁之后能够再次获取该锁而不会被锁所阻塞,该特性的实 现需要解决以下两个问题。 1)线程再次获取锁。锁需要去识别获取锁的线程是否为当前占据锁的线程,如果是,则再 次成功获取。 2)锁的最终释放。线程重复n次获取了锁,随后在第n次释放该锁后,其他线程能够获取到 该锁。锁的最终释放要求锁对于获取进行计数自增,计数表示当前锁被重复获取的次数,而锁 被释放时,计数自减,当计数等于0时表示锁已经成功释放。

  • 公平锁和非公平锁

公平性与否是针对获取锁而言的,如果一个锁是公平的,那么锁的获取顺序就应该符合请求的绝对时间顺序,也就是FIFO。从代码实现上来说,非公平锁只要CAS设置同步状态成功就表示当前线程获取了锁,而公平锁还需要多判断同步队列中的当前节点是否还有前驱节点,如果存在,则表示有线程更早的请求获取锁。

在AQS中体现非公平锁以及公平锁。

公平锁:当同步队列中的头节点释放同步状态时,新线程不能跟头节点的后置节点的线程争夺同步状态的获取,而是要new成一个node节点 尾插到同步队列中。

非公平锁:当同步队列中的头节点释放同步状态时,新线程可以跟头节点的后置节点争夺同步状态,如果争夺失败 新线程才会new成node节点 尾插到同步队列。

  • Conditon (主要包括等待队列、等待和通知方法)

Condition定义了等待/通知两种类型的方法,当前线程调用这些方法时,需要提前获取到 Condition对象关联的锁。Condition对象是由Lock对象(调用Lock对象的newCondition()方法)创建出来的,且一个lock对象可以有多个condition对象,每个condition对象都是对应一个单独唯一的条件。

等待队列:等待队列是一个FIFO且单向的队列,在队列中的每个节点都包含了一个线程引用,该线程就是在Condition对象上等待的线程。

Condition拥有首尾节点的引用,而新增节点只需要将原有的尾节点nextWaiter 指向它,并且更新尾节点即可。上述节点引用更新的过程(addConditionWaiter)并没有使用CAS保证,原因在于调用 await()方法的线程必定是获取了锁的线程,也就是说该过程是由锁来保证线程安全的。

等待:调用Condition的await()方法(或者以await开头的方法)的线程成功获取了锁的线程,也就是同步队列中的首节点,该方法会将当前 线程构造成节点并加入等待队列中,然后释放同步状态,唤醒同步队列中的后继节点,然后当前线程会进入等待状态。如果从队列的角度去看,当前线程加入Condition的等待队列,同步队列的首节点并不会直接加入等待队列,而是通过addConditionWaiter()方法把当前线程构造成一个新的节点并将其加入等待队列中,当从await()方法返回时,当前线程一定获取了Condition相 关联的锁。

通知:通过调用同步器的enq(Node node)方法,等待队列中的头节点线程安全地移动到同步队列。当节点移动到同步队列后,当前线程再使用LockSupport工具类唤醒该节点的线程。 被唤醒后的线程,将从await()方法中的while循环中退出(isOnSyncQueue(Node node)方法 返回true,节点已经在同步队列中),进而调用同步器的acquireQueued()方法加入到获取同步状态的竞争中。 成功获取同步状态(或者说锁)之后,被唤醒的线程将从先前调用的await()方法返回,此时该线程已经成功地获取了锁。

Condition的signalAll()方法,相当于对等待队列中的每个节点均执行一次signal()方法,效 果就是将等待队列中所有节点全部移动到同步队列中,并唤醒每个节点的线程。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值