AQS是JUC的核心,明白AQS基本上JUC下的工具类就会使用:
AQS其实就是CLH队列(很想双向链表+state状态)
故事背景:最近看七龙珠
以ReentrantLock为例
package JUC;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
public class test {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
new Thread(() -> {
lock.lock();
try {
System.out.println("沙鲁与孙悟空");
//战斗的时间,
try { TimeUnit.MINUTES.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
}finally {
lock.unlock();
}
},"Thread孙悟空").start();
new Thread(() -> {
lock.lock();
try {
System.out.println("沙鲁与贝吉塔");
//战斗的时间,
try { TimeUnit.MINUTES.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
}finally {
lock.unlock();
}
},"Thread贝吉塔").start();
new Thread(() -> {
lock.lock();
try {
System.out.println("沙鲁与孙悟饭");
//战斗的时间,
try { TimeUnit.MINUTES.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
}finally {
lock.unlock();
}
},"Thread孙悟饭").start();
}
}
简单说一下:ReentrantLock
//下面 删掉
简单说一下:AbstractQueuedSynchronizer
public abstract class AbstractQueuedSynchronizer
extends AbstractOwnableSynchronizer
implements java.io.Serializable {
//AbstractOwnableSynchronizer是一个抽象类其实就一个地方用到,后面会有
private static final long serialVersionUID = 7373984972572414691L;
/**
* Creates a new {@code AbstractQueuedSynchronizer} instance
* with initial synchronization state of zero.
*/
protected AbstractQueuedSynchronizer() { }
/**
* Wait queue node class.
*
* <pre>
* +------+ prev +-----+ +-----+
* head | | <---- | | <---- | | tail
* +------+ +-----+ +-----+
* </pre>
*/
//这个Node类相当于链表中的节点
//volatile Node prev; 前指针
//volatile Node next; 后指针
//volatile int waitStatus; 节点的状态
//后面是删减版
static final class Node {
static final Node SHARED = new Node();
static final Node EXCLUSIVE = null;
volatile int waitStatus;
volatile Node prev;
volatile Node next;
volatile Thread thread;
Node nextWaiter;
final boolean isShared() {
return nextWaiter == SHARED;
}
Node() { // Used to establish initial head or SHARED marker
}
Node(Thread thread, Node mode) { // Used by addWaiter
this.nextWaiter = mode;
this.thread = thread;
}
Node(Thread thread, int waitStatus) { // Used by Condition
this.waitStatus = waitStatus;
this.thread = thread;
}
}
//这个不属于链表的,这个算是上下 链表属于左右,看图就能明白
private transient volatile Node head;
private transient volatile Node tail;
//这个状态是 是否有线程占用lock锁,没线程占用就可以直接获得,不需要进入队列(双向链表)中等待
private volatile int state;
a.步骤如下:
Thread孙悟空刚刚来到其他人还没有来,发现state = 0,可以直接占用
步骤1:lock.lock(),调用ReentrantLock类的lock()方法
步骤2:lock()方法内调用的Sync类的lock方法() 是一个抽象方法 //这个Sync继承了AbstractQueuedSynchronizer(AQS)
步骤3:lock是抽象方法,有两个实现方法 一个是公平锁FairSync(true)一个是非公平锁NonfairSync(默认是非公平锁,false 为非公平锁)
步骤4:非公平锁的实现,AQS 就是 state + CLH ,当代码第一次走到这里时,state = 0
compareAndSetState(0,1) CAS操作,期待state = 0 ,如果为0 把state比较并交换为1 ,设置独占线程set Exclusive Owner Thread为Thread孙悟空
protected final void setExclusiveOwnerThread(Thread thread) {
exclusiveOwnerThread = thread; //独占线程 为 Thread 孙悟空
}
b.步骤如下:
Thread贝吉塔为第二个来到的人,发现沙鲁正在和Thread孙悟空正在战斗(state = 1),也就是说Thread贝吉塔尝试获取lock(沙鲁)失败,进入阻塞队列中
前三步和Thread孙悟空一样,第四部CAS失败if(false) 走 else {
acquire(1); // 这个1 表示 重复次数,重入锁与这个数值有关
}里面,进入第五步
步骤5 acquire方法是AQS内方法,传参arg为1
步骤6 tryAcquire属于设计模式中模板设计模式是一个钩子方法,子类必须实现否则会抛出异常
步骤7 NonfairSync非公平锁实现,其实就是ReentrantLock(子类)去实现了tryAcquire方法
步骤8 return NonfairTryAcquire(1),方法在ReentrantLock有具体实现过程
步骤9 详细介绍如下
final boolean nonfairTryAcquire(int acquires) {
//获取当前线程 即Thread贝吉塔
final Thread current = Thread.currentThread();
//获取state的值,现在为1,因为被Thread孙悟空占用
int c = getState();
//判断state 是否为0 当然有可能为0只要在这一时刻Thread孙悟空释放lock锁,就可以重启抢到lock锁,就是是state的状态为0时,当然这样的概率 和中彩票一样的
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current); //在中彩票的情况下,把独占线程设置为Thread贝吉塔
return true;
}
}
//这里判断自己是否是占用lock,如果是自己 state++(加一表示重入一次),也是ReentrantLock具有可重入性的原因
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires; //acquires为已经重入多少次
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
//一般以false结束
return false;
}
步骤10:return false的返回结果,!tryAcquire(arg)为true,执行&&acquireQueued(addWaiter(Node.EXCLUSIVE),arg) 当然先执 addWaiter(Node.EXCLUSIVE)
步骤11:执行addWaiter
private Node addWaiter(Node mode) {
//这个node为当前线程,第一次执行为node为Thread贝吉塔
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
//第一次执行时,tail为null ,因为现在CLH还没有真正连接,所以CLH的tail指向为null
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
//第一次执行直接会跳到这里
enq(node);
return node;
}
步骤12: enq入队操作
这个就是很优雅的代码,是形成CLH队列的代码
private Node enq(final Node node) {
for (;;) {
//第一次执行tail为null,所以t为null
//::::第二次 t = head = tail
Node t = tail;
if (t == null) { // Must initialize
//compareAndSetHead(new Node())
//new Node()为哨兵节点
if (compareAndSetHead(new Node())) // 这里就是设置一个new Node() 为Head,
//下面这三行代码是我截取的compareAndSetHead 下面的代码
/*
private final boolean compareAndSetHead(Node update) {
return unsafe.compareAndSwapObject(this, headOffset, null, update);
}
*/
//第一次执行是tail的地址和head的地址完全相等
tail = head;
} else {
//::::node.prev = head 就是node的前指针指向head (Thread贝吉塔 的prev 的前指针指向 head(head 为一个哨兵节点))
node.prev = t; //只有第一次head == tail (当只存在哨兵节点时),后面tail队列中最后一位Node节点
//::::设置tail为node地址,这里和第一次不一样
if (compareAndSetTail(t, node)) { /* cas 一定为比较并交换 这里我猜是发生了
完全看不懂下面的代码,只有tail.next = node 这样才能形成 CLH 队列
private final boolean compareAndSetTail(Node expect, Node update) {
return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
}
*/
//::::t.next = tail
t.next = node; //tail 指向 node 节点
return t;
}
}
}
}
步骤13:
acquireQueued(final Node node, int arg) {}
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
//取Thread贝吉塔的前置Node(这里是哨兵节点)
final Node p = node.predecessor();
//tryAcquire(arg)再次尝试获取lock,这里
if (p == head && tryAcquire(arg)) {
setHead(node); //这个在unpark下执行 他写的代码是真的优雅,这个如果线程贝吉塔获得 lock 那么让哨兵节点的next指向 null
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
步骤14:
//获取node前一个节点,Thread贝吉塔的前一个节点为哨兵节点
final Node predecessor() throws NullPointerException {
Node p = prev;
//不存在会抛出异常
if (p == null)
throw new NullPointerException();
else
return p;
}
步骤15: 第一次,把ws的值变成了-1
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
//::::表示第二次,看步骤13就知道在for循环内
//第一次ws = 0;
int ws = pred.waitStatus;
if (ws == Node.SIGNAL) //Node.SIGNAL 为-1
//::::第二次返回true
/*
* This node has already set status asking a release
* to signal it, so it can safely park.
*/
return true;
if (ws > 0) {
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
*/
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
/*
* waitStatus must be 0 or PROPAGATE. Indicate that we
* need a signal, but don't park yet. Caller will need to
* retry to make sure it cannot acquire before parking.
*/
//第一次,把ws的值变成了-1
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
步骤16: 阻塞Thread线程
private final boolean parkAndCheckInterrupt() {
//Thread贝吉塔被park阻塞,休眠中(这个是关键点,后面怎样唤醒Thread贝吉塔线程才是关键)
LockSupport.park(this);
return Thread.interrupted();
}
同理:Thread孙悟饭也会加入阻塞队列中
接下来就是lock.unlock()阶段,当Thread孙悟空释放lock(不在于沙鲁战斗时),Thread贝吉塔和Thread孙悟饭线程获得lock的呢
步骤1:调用sync.release(1)
步骤2: 调用tryRelease(1)
步骤3: 调用
UnsupportedOperationException(); 这个是模板方法,必须在子类中实现
步骤4 :tryRelease
protected final boolean tryRelease(int releases) {
//getState就是与state的值,releases = 1 所以c等于0
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
//设置占用Lock为null state = 0,这样就可以有其他线程获得锁了(与沙鲁战斗的资格)
setExclusiveOwnerThread(null);
}
//设置state为0
setState(c);
return free; //返回true
}
步骤5返回true
public final boolean release(int arg) {
if (tryRelease(arg)) { //返回true
//h为哨兵节点
Node h = head;
if (h != null && h.waitStatus != 0)
//上面条件都成立
unparkSuccessor(h);
return true;
}
return false;
}
步骤6:
private void unparkSuccessor(Node node) {
/*
* If status is negative (i.e., possibly needing signal) try
* to clear in anticipation of signalling. It is OK if this
* fails or if status is changed by waiting thread.
*/
int ws = node.waitStatus;
if (ws < 0)
//这里哨兵节点status变成了0
compareAndSetWaitStatus(node, ws, 0);
/*
* Thread to unpark is held in successor, which is normally
* just the next node. But if cancelled or apparently null,
* traverse backwards from tail to find the actual
* non-cancelled successor.
*/
//s 为Thread贝吉塔
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
//这个if成立,unpark(Thread贝吉塔)
if (s != null)
LockSupport.unpark(s.thread);
}
回到lock的 步骤16:Thread贝吉塔成功被唤醒
步骤17:
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
//lock步骤6、7、8、9 先看一下下面的解析
if (p == head && tryAcquire(arg)) {
//设置head指向node
/*
这里直接吧setHead方法复制过来了
private void setHead(Node node) {
//设置head为node(说指向也可以,感觉都能理解,head指向node就是Thread贝吉塔(之前用用过的)地址变成了哨兵节点,当然真正的Thread贝吉塔已经抢到了lock(正在和沙鲁战斗))
head = node;
//彻底变为null
node.thread = null;
node.prev = null;
}
*/
setHead(node);
//p为以前的哨兵节点.next说明被断开,这个哨兵已经不合任何节点相连
p.next = null; // help GC GC垃圾回收 断开哨兵节点的next为null ,GC root 为空,在GC时会被回收
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
步骤17会用到lock步骤6、7、8、9,这里不粘贴图了
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
//设置state =1
if (compareAndSetState(0, acquires)) {
//lock的占用线程为Thread贝吉塔
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;
}
整个故事就是这样
为什么是非公平锁,似乎是公平的呀,因为后面来的线程有可能比先来的tryAcquire获得锁,我理解是这样,存在问题请指出