并发-AQS同步队列与等待队列

1. AQS

        队 列同步器 AbstractQueuedSynchronizer (以下 同步器 ),是用来构建 或者其他同步 组 件的基础 框架,它使用了一个 int 员变 量表示同步状 ,通 内置的 FIFO队列 来完成 获 取线 程的排 工作。同步器提供的3 个方法(getState() setState(int newState) compareAndSetState(int expect,int update) )来 行操 作,因为 的改 是安全的。子 推荐被定 义为 自定 同步 件的静 内部 类,同步器自身没有 实现 任何同步接口,它 仅仅 是定 了若干同步状 态获 取和 放的方法来 供自定义 同步 件使用,同步器既可以支持独占式地 取同步状 ,也可以支持共享式地 获 取同步状态 这样 就可以方便 实现 不同 型的同步 件( ReentrantLock 、 ReentrantReadWriteLock和 CountDownLatch 等)。

2.Object的监视器模型(Synchronization应该是这么玩的)

并发-AQS同步队列与等待队列_第1张图片

3. 同步队列

3.1 同步器队列基本构造(一个同步队列 + 多个等待队列)

并发-AQS同步队列与等待队列_第2张图片

TODO:多个等待队列的话,节点怎么选择其中一个等待队列加入呢

EXPLAIN TODO:

任意一个Java对象,都拥有一组监视器方法(定义在java.lang.Object上),主要包括wait()、notify()以及notifyAll()方法,这些方法与synchronized同步关键字配合,可以实现等待/通知模式。

JUC包提供了Condition来对锁进行精准控制,Condition是一个多线程协调通信的工具类,可以让某些线程一起等待某个条件(condition),只有满足条件时,线程才会被唤醒。Condition对象是由Lock对象(调用Lock对象的newCondition()方法)创

建出来的。condition中两个最重要的方法:
  • await,把当前线程阻塞挂起;
  • signal,唤醒阻塞的线程

3.2 Node节点属性如下

static final class Node {
    //表示节点的状态,包含SIGNAL、CANCELLED、CONDITION、PROPAGATE、INITIAL
    volatile int waitStatus;
    //前继节点
    volatile Node prev;
    //后继节点
    volatile Node next;
    //当前线程
    volatile Thread thread;
    //存储在condition队列中的后继节点
    Node nextWaiter; 
}

waitStatus节点的几种状态:

  • CANCELLED,值为1,由于在同步队列中等待的线程等待超时或者被中断,需要从同步队列中取消等待,节点进入该状态将不会变化;
  • SIGNAL,值为-1,后继节点的线程处于等待状态,而当前节点的线程如果释放了同步状态或者被取消,将会通知后继节点,使后继节点得以运行;
  • CONDITION,值为-2,节点在等待队列中,节点线程等待在Condition上,当其他线程对Condition调用了signal()方法后,该节点将会从等待队列中转移到同步队列中,加入到对同步状态的获取中;
  • PROPAGATE,值为-3,表示下一次共享式同步状态获取将会无条件地被传播下去;
  • INITAL,值为0,初始状态;

3.3 同步队列如何保证线程安全

  • CAS设置首节点和尾节点
  • 节点自旋获取同步状态

TODO: 待敲打

3.3 Lock获取锁流程

        在获取同步状态时,同步器维护一个同步队列,获取状态失败的线程都会被加入到队列中并在队列中进行自旋;移出队列(或停止自旋)的条件是前驱节点为头节点且成功获取了同步状态。在释放同步状态时,同步器调用tryRelease(int arg)方法释放同步状态,然后唤醒头节点的后继节点。

AQS_acquire.png

上图画的有点问题:

等待状态是否大于0 -> 移除该前驱节点时,边的关系是 “”, 而不是“

TODO:等待状态如果是Node.SIGNAL, 为什么直接阻塞了

 4. 等待队列

4.1 等待

调用condition的await方法,将会使当前线程进入等待队列并释放锁(先加入等待队列再释放锁),同时线程状态转为等待状态。

从同步队列和阻塞队列的角度看,调用await方法时,相当于同步队列的首节点移到condition的等待队列中
在这里插入图片描述



4.2 通知

调用condition的signal方法时,将会把等待队列的首节点移到同步队列的尾部,然后唤醒该节点。被唤醒,并不代表就会从await方法返回,也不代表该节点的线程能获取到锁,它一样需要加入到锁的竞争acquireQueued方法中去,只有成功竞争到锁,才能从await方法返回

在这里插入图片描述

参考资料:

【Java中锁的自我总结(同步队列、等待队列)】

【Java 中队列同步器 AQS(AbstractQueuedSynchronizer)实现原理】

【并发之AQS原理(三) 如何保证并发】

【Lock原理分析】

【同步器AQS中的同步队列与等待队列】

你可能感兴趣的:(并发,多线程)