Aqs源码解析(一)
1.什么是Aqs
Aqs是java中的一个抽象类,全名AbstractQueuedSynchronizer,几乎juc(java.util.concurrent)包中大部分的实现都依赖于此类。比如ReentrantLock、CountdownLatch、Condition、Semaphore(后续会逐一分析每个类)等等。
2.Aqs分析之ReentrantLock
2.1什么是ReentrantLock
ReentrantLock是java层面实现的排他锁,其支持公平锁和非公平锁。此锁实现了Lock接口,提供lock、unlock等方法。
2.2ReentrantLock的使用
package com.example.demo;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockDemo {
static int lockValue = 1;
static int value = 1;
//构造方法传入true或false true为公平锁false为非公平锁
static Lock lock = new ReentrantLock(true);
static void testLock(){
lock.lock();
try {
if(lockValue==1){
System.out.println(Thread.currentThread().getName()+":lockValue==1");
}else{
System.out.println(Thread.currentThread().getName()+":lockValue!=1");
}
TimeUnit.SECONDS.sleep(1);
lockValue++;
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
static void test(){
try {
if(value==1){
System.out.println(Thread.currentThread().getName()+":value==1");
}else{
System.out.println(Thread.currentThread().getName()+":value!=1");
}
TimeUnit.SECONDS.sleep(1);
value++;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
new Thread(()->{
testLock();
},"lock1").start();
new Thread(()->{
testLock();
},"lock2").start();
new Thread(()->{
test();
},"thread1").start();
new Thread(()->{
test();
},"thread2").start();
TimeUnit.SECONDS.sleep(5);
}
}
上述代码简单我就不过多的解释了
2.3锁升级的概念
在分析源码之前,我觉得很有必要梳理清楚什么是锁升级,锁是如何升级的,这么做有什么好处,他的场景是什么。
1.什么是锁升级
所谓的锁升级简单来说是锁的一个优化过程,没有锁升级之前的加锁过程是,当线程未抢占到锁时,将会被park挂起,而对于挂起这一操作而言是非常耗性能的,所以在以这个为背景的前提下,对锁进行了优化,也就是所谓的锁升级。
2.锁是如何升级的
锁升级一共包含3个步骤,依次为重入、自旋、挂起。
重入:指的是一个线程抢到锁后再次调用加有锁的方法,这个时候线程不需要去抢锁只需要在原来的基础上将抢锁次数加一即可。
自旋:指的是当线程未抢到锁时,不会立即被挂起,而会在挂起之前再次进行抢锁操作(一个加了锁的方法可能执行的非常的快,可能在未挂起之前上次加有锁的线程就执行完毕且释放了锁,这个时候若直接抢到了锁就避免了挂起带来的性能开销)
挂起:当线程经过上面两个步骤之后仍然没有抢到锁,则会被挂起,当前线程处于BLOCKED状态
源码分析
构造方法
public ReentrantLock(boolean fair) {
//传入fair标识 代表公平锁或者非公平锁
sync = fair ? new FairSync() : new NonfairSync();
}
sync解释:
sync为ReentrantLock中的匿名内部类
abstract static class Sync extends AbstractQueuedSynchronizer {}
其中FairSync和NonfairSync为其实现,这里我们只分析公平锁
static final class FairSync extends Sync {}
lock方法
public void lock() {
//lock方法调用的为公平锁或者非公平锁的lock方法
sync.lock();
}
公平锁的lock方法实现
final void lock() {
acquire(1);
}
public final void acquire(int arg) {
//tryAcquire(arg) 尝试抢锁
//addWaiter(Node.EXCLUSIVE), arg) 构造一个独占锁标识的队列节点
//acquireQueued() 自旋抢锁 或者挂起 回应中断操作
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
方法解释
tryAcquire(arg) :尝试抢锁
addWaiter(Node.EXCLUSIVE), arg) :构造一个独占锁标识的队列节点
acquireQueued(): 自旋抢锁 或者挂起 回应中断操作
selfInterrupt():回应中断
一个一个来分析
1.tryAcquire方法:
protected final boolean tryAcquire(int acquires) {
//1.获取当前线程
final Thread current = Thread.currentThread();
//2.或者state这个变量(记住这个变量)
int c = getState();
//3.如果state为0则标识当前无线程获取锁状态
if (c == 0) {
//4.hasQueuedPredecessors会去判断aqs队列中是否有排队节点
//因为这是公平锁所以如果判断aqs队列中有排队节点则会直接返回false(标识抢锁失败)
//若没有排队节点,则会进行cas操作将当前的status设置为1
//若在做cas操作时 有其他线程已经抢到了锁(将status设置为了1)
//则会返回false表示抢锁失败
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
//将当前线程设置为抢到了锁的线程 并返回true 表示抢锁成功
setExclusiveOwnerThread(current);
return true;
}
}
//如果当前线程为已抢到锁的线程(这里就是上面说的重入的概念)
else if (current == getExclusiveOwnerThread()) {
//则将status+1 并且返回true 标识抢锁成功
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}