简介
AQS:AbstractQueuedSynchronizer,是JUC下lock包的一个类,能够多线程访问共享资源的同步器框架。
首先介绍AQS中一些重要的参数:
state:同步器的状态,
exclusiveOwnerThread:互斥锁持有的线程
CLH队列:这是一个双向链表队列
head:同步等待期的头部
tail:同步等待器的尾部
在AQS中线程访问资源有两种方式:
使用独占锁:顾名思义每次访问只能有一个线程持有锁,ReentrantLock就是以独占方式实现的互斥锁。
使用共享锁:共享锁就是允许多个线程痛死获得锁,并发访问资源。比如:ReadWriteLock
ReentrantLock可重入锁:
线程在ReentrantLock的lock方法后,获取到锁没有释放锁,当当前线程再次调用lock方法时,并不会阻塞,只会增加重入的次数。
首先我们通过一段代码逐步分析AQS的源码:
public class AQS {
private ReentrantLock lock = new ReentrantLock();
public void lock1(){
lock.lock();
System.out.println(Thread.currentThread().getName() +"获得了锁");
lock2();
lock.unlock();
}
public void lock2(){
lock.lock();
System.out.println(Thread.currentThread().getName() +"获得了锁");
lock.unlock();
}
public static void main(String[] args) {
AQS t = new AQS();
Thread t1 = new Thread(()->t.lock1(),"t1");
t1.start();
}
}
从代码的执行结果来看,t1线程在调用lock1方法后并没有释放锁就调用了,lock2方法,但是并没有出现阻塞现象。
AQS源码分析:
我们根据上述代码逐步分析:
//默认的无参构造器
public ReentrantLock() {
sync = new NonfairSync();
}
//有参构造器
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
首先看一下ReentrantLock类的构造方法,可以看出关键的类sync(抽象类Sync也是ReentrantLock类中的属性),这也就引出了公平锁和非公平锁的概念
//抽象类Sync继承了AbstractQueuedSynchronizer(也就是常说的AQS)
abstract static class Sync extends AbstractQueuedSynchronizer
此处我们可以先熟悉一下ReentrantLock和AbstractQueuedSynchronizer的相关的结构:
跟着上述demo,我们继续调试Lock()方法:
public void lock() {
sync.lock();
}
//非公平
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
//公平
final void lock() {
acquire(1);
}
我们通过前面可以知道,ReentrantLock的空构造方法是构造一个非公平锁的Sync实体类:
public ReentrantLock() {
sync = new NonfairSync();
}
所以上面代码的lock()方法,执行的是NonfairSync类中的lock()方法。
compareAndSetState(0, 1)
这个就是我们常说的CAS,
CAS相关源码:
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
//获取state在内存中的偏移量
stateOffset = unsafe.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("state"));
这个方法底层调用的是,unsafe类下的compareAndSwapInt()方法,参数中stateOffset是state变量在内存中的偏移量,通过这个偏移量可以获取到state的值,unsafe类中的方法都具有原子性,保证了线程安全,compareAndSwapInt()方法,如果state==expect,那么就将state更新为update的值,并返回true;一开始state的值为0,传入的expect为0,update为1,此时state等于expect,则把state赋值为1,并返回true。
在非公平的lock()方法中,首先会尝试使用CAS方式加锁,如果成功了就使用
setExclusiveOwnerThread(Thread.currentThread())
//AbstractOwnableSynchronizer类下的
protected final void setExclusiveOwnerThread(Thread thread) {
exclusiveOwnerThread = thread;
}
方法,将当前线程赋给exclusiveOwnerThread,这也是非公平锁的含义,每个线程加锁时,会先尝试加锁,如果成功就赋给exclusiveOwnerThread,也就不用放在CLH队列中进行排队。
CLH同步队列:是一个FIFO双向队列,AQS主要是通过其实现公平锁。线程通过AQS获取锁失败就会将线程封装成一个Node节点,插入队列尾部,当有线程释放锁时,就会尝试将锁头的next节点占用锁。
//首先tryAcquire获取锁,如果获取到,!tryAcquire()为false,后面不用执行,直接执行业务代码
//如果为获取到锁,添加一个新的节点,插入到CLH队列尾部
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
//*****核心:主要功能是完成头节点尝试获取锁资源,以及其他节点被阻塞
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
//获取当前节点的前一个节点
final Node p = node.predecessor();
//只有P节点是头节点才可以获取锁资源,如果获取到锁资源也就不需要阻塞
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
//p节点不是第一个节点,或者p节点尝试加锁失败,
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
// 阻塞当前节点,等待被unpark()方法唤醒
/**private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
**/
}
} finally {
if (failed)
cancelAcquire(node);
}
}
//在CLH的队列结尾处添加一个独占尾节点
private Node addWaiter(Node mode) {
//把当前线程构造一个新的节点
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
//判断当前尾节点是否为null,不为null说明队列中有节点
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
//尾插的方式插入当前节点
pred.next = node;
return node;
}
}
//如果对了为空,将队列初始化后插入当前节点
enq(node);
return node;
}
//这是为了确保节点插入队列的尾部,防止CAS失败操作,也会保证节点插入队列
private Node enq(final Node node) {
for (;;) {
Node t = tail;
//尾节点为空,也就是队列为空
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
//此时CLH队列不为空,尾插
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
公平锁和非公平锁的区别就是在tryAcquire()方法上,多了一个hasQueuedPredecessors()方法。这个方法主要是寻找CLH队列中有没有,比当前节点等待获取锁的时间更长。
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}