先总字节跳动及其它大厂面试题说起
- ReentantLock实现原理,简单说一下AQS?
- synchronized的锁优化过程,详细说一下吧,偏向锁和轻量级锁有什么区别?
前置知识
公平锁和非公平锁
可重入锁
LockSupport
自旋锁
数据结构之链表
设计模式之模板设计模式
是什么
字面意思
抽象的队列同步器
源代码
public abstract class AbstractQueuedSynchronizer
extends AbstractOwnableSynchronizer
implements java.io.Serializable {
技术解释
是用来构建锁或者其他同步器组件的重量级基础框架及整个JUC体系的基石,通过内置的FIFO队列来完成资源获取,线程的排队工作,并通过一个int类型变量表示持有锁的状态。
CLH:Craig、Landin 和 Hagersten队列,是一个单向链表,AQS中的队列是CLH变体的虚拟双向队列FIFO.
AQS为什么是JUC内容中最重要的基石
和AQS有关的类
- ReentrantLock
- CountDownLatch
- ReentrantReadWriteLock
- Semaphore
- ……
通过查看几个累的源代码可以看出底层都封装一个AbstactQueuedSynchronizer;所以这个类是JUC的基石。
进一步理解锁和同步器的关系
锁:面向锁的使用者,定义了程序员和锁交互的使用层API,隐藏了实现细节,你调用即可。lock.lock();
同步器:面向锁的实现者,比如Java并发大神DougLee,提出统一规范并简化了锁的实现,屏蔽了同步状态管理、阻塞线程排队和通知、唤醒机制等。
能干嘛
加锁会导致阻塞,有阻塞就需要排队,实现排队必然需要有某种形式的队列来进行管理
解释说明:
抢到资源的线程直接使用处理业务逻辑,抢不到资源的必然涉及一种排队等候机制。抢占资源失败的线程继续去等待(类似银行业务办理窗口都满了,暂时没有受理窗口的顾客只能去候客区排队等候),但等候线程仍然保留获取锁的可能且获取锁流程仍在继续(候客区的顾客也在等着叫号,轮到了再取受理窗口办理业务)。
既然说到了队列等候机制,那么就一定会有某种队列形成,这样的队列是什么数据结构呢?
如果共享资源占用,就需要一定的阻塞等待唤醒机制来保证锁分配。这个机制主要用的是CLH队列的变体实现的,将暂时获取不到锁的线程加入到队列中,这队列就是AQS的抽象表现。他将请求共享资源的线程封装成队列的节点(Node),通过CAS、自旋以及LockSupport.park()的方式,维护state变量的状态,使并发达到同步的控制效果。
AQS初步
AQS初识
官网解释
有阻塞就需要排队,实现排队必然需要队列
AQS使用一个volatile的int类型的成员变量来表示同步状态,通过内置的FIFO队列来完成资源获取的排队工作,将每条要去抢占资源的线程封装成一个Node节点来实现锁的分配,通过CAS完成对State值的修改。
static final class Node {
volatile Node prev; //上一个节点指针
volatile Node next; //下一个节点指针
volatile Thread thread;//封装的等待线程
.....
}
//队列的头部指针
private transient volatile Node head;
//队列的尾部指针
private transient volatile Node tail;
//同步的状态变量
private volatile int state;
AQS内部体系架构
AQS自身
AQS的int变量
AQS的同步状态state成员变量,private volatile int state;
银行办理业务的受理窗口状态,零就是没有人,自由状态可以办理;大于等于1,有人占用窗口,等着取号
AQS的CLH队列
小总结
有阻塞就需要排队,实现排队必然需要队列
state变量+CLH变种的双端队列
内部类Node
Node的int变量: Node的等待状态waitStatus成员变量:volatile int waitStatus;
等候区其他顾客(其他线程)的等待状态,队列中每个排队的个体就是一个Node
AQS同步队列的基本结构
static final class Node {
/** 共享模式 Marker to indicate a node is waiting in shared mode */
static final Node SHARED = new Node();
/** 独占模式 Marker to indicate a node is waiting in exclusive mode */
static final Node EXCLUSIVE = null;
/** 线程被取消了 waitStatus value to indicate thread has cancelled */
static final int CANCELLED = 1;
/** 后续线程需要唤醒 waitStatus value to indicate successor's thread needs unparking */
static final int SIGNAL = -1;
/** 等待condition唤醒 waitStatus value to indicate thread is waiting on condition */
static final int CONDITION = -2;
/**
* waitStatus value to indicate the next acquireShared should
* unconditionally propagate
* 共享式同步状态获取将会无条件地传播下去
*/
static final int PROPAGATE = -3;
//初始为0,状态是上面的几种
volatile int waitStatus;
//前指针
volatile Node prev;
//后指针
volatile Node next;
//等待线程
volatile Thread thread;
//........
}
从我们的ReentrantLock开始解读AQS
见下一章笔记。