AQS 数据结构
数据结构分为两部分。第一行的同步队列,用于排队所有的的等待线程一次获取锁。第二、三行是条件队列,每一行对应当前lock锁的一个条件Condition。
还有一部分没有画出来,是条件队列中节点在signal()方法中会enq入队到第一行的同步队列中去获取锁,这时条件队列的node节点中的prev、next会指向同步队列的节点上。
同步队列(sync queue)
基于双向链表的同步队列,头尾指针head、tail。同步队列的修改是线程安全的,因为会有多线程的竞争。
每一个node在同步队列中都代表一个正在等待获取锁的线程,也是首次获取锁失败后才进入同步队列进行等待的线程。第一个node代表当前获得锁的线程,由head指向首个节点。
重要属性解析:
- prev指针作用:prev == head 成功,是去争夺锁的前提条件。只有当一个node的prev已经是head时,该node才有可能获得锁,即就是锁的获取过程遵从sync queue的顺序排列过程。
- next指针作用:当prev在释放锁的时候,通过next指针找到下一个需要获得锁的节点及其对于线程。
- waitStatus:这个属性比较重要,0是默认值,和PROPATION一样是指要向后传递,但PROPATION只设置在首节点(即head);CONDITION指该node为一个等待信号的节点,常用在条件队列中;CANCELED是指当前node被取消了,可能出现在同步队列和等待队列中(被取消的原因可能由释放锁失败,此作用在await()#fullRelease()方法中,后续会讨论)。
node有五个最重要的属性:
- prev:前一个节点指针
- next:后一个节点指针
- waitStatus:当前节点的等待状态,有0(默认)、PROPAGATION、SIGNAL、CONDITION、CANCELED 五种
- thread:当前排队的线程
- nextWaiter:mode,有三种值Node.SHARED,Node.EXCLUSIVE,条件队列中下一个node的指针
node两种节点类型:
- EXECLUDE:排它锁
- SHARED:共享锁
条件队列(condition queue)
基于单向链表的非同步队列,头尾指针为firstWaiter、lastWaiter。条件队列为非同步队列,修改条件队列的线程必须获得当前锁lock,故不存在多线程竞争。
node有五个重要属性:
- nextWaiter:条件队列中下一个节点指针
- thread:当前排队的线程
- prev:保留,后面指向同步队列的某节点上
- next:保留,后面指向同步队列的某节点上
- waitStatus:条件队列只有 CONDITION 一种值
节点类型:
- 此时waitStatus就是指针,没有特殊类型
条件队列节点enq到同步队列
条件队列的原理就是,先调用await() 让当前线程进入到某个条件的条件队列中睡眠等待,直到signal信号唤醒后,争夺锁成功返回继续执行await()后面的代码,以此达到最终一种 等待-唤醒
模式实现线程间通信协作。
而条件队列节点被唤醒后若想重新获取到锁,一定要先node进入到sync queue中排队获取排它锁,将来才有可能等到锁。因为此时可能会有线程已经获得锁,同时有很多线程已经在等待获取锁了,故只要一样加入等待锁的同步队列即可。
AQS都有哪些特点:
这里会先汇总AQS的一些特点以及都包含了哪些类型的锁,有一个整体的认知。
- 共享锁、排它锁
- 公平锁、非公平锁
- 阻塞式锁、非阻塞式锁、超时锁
- 可中断锁、不可中断锁