Condition案例使用及其源码解析
文章目录
前言
必须先看懂ReentrantLock才能看这个Condition,因为condition是配套ReentrantLock来使用的.如下.
AQS是什么?基于ReentrantLock解密!
下面会基于ReentrantLock的加锁和释放锁来结合Condition的阻塞释放锁以及唤醒加锁进行使用讲解以及源码解析
老规矩,还是要一步一图,不然是看不懂的.
condition案例-结合ReentrantLock
- 代码如下,首先需要明确:condition使用的await()方法相当于wait()方法,signal()方法相当于notify()方法
代码如下
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class TestReentrantLockAndConditionDemo {
static ReentrantLock reentrantLock=new ReentrantLock();
static Condition condition = reentrantLock.newCondition();
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
try {
reentrantLock.lock();
System.out.println("线程1加锁成功");
System.out.println("线程1开始阻塞");
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
reentrantLock.unlock();
System.out.println("线程1释放锁成功");
}
});
thread1.start();
Thread.sleep(1000);
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程2开始加锁");
reentrantLock.lock();
System.out.println("线程2加锁成功");
condition.signal();
System.out.println("线程2释放线程1");
reentrantLock.unlock();
System.out.println("线程2释放锁完成");
}
});
thread2.start();
}
}
结果展示
线程1加锁成功
线程1开始阻塞
线程2开始加锁
线程2加锁成功
线程2释放线程1
线程2释放锁完成
线程1释放锁成功
结果分析
- 只要线程1被await,那么就会被放入condition等待队列里面,然后释放锁,等待被唤醒
- 此时线程2过来进行加锁,然后最后释放线程1调用signal,线程1会从condition队列维护的阻塞队列node最终会转移到AQS中的阻塞队列里面.继续等待线程2释放锁
- 线程2释放锁,之后,线程1会重新从阻塞队列里面释放出来进行加锁.
基于这个案例我们可以通过一步一图的形式去查看ReentrantLock和condition的源码
condition和ReentrantLock源码分析
- 首先ReentrantLock的加锁lock方法,先看上面案例的线程1加锁lock
lock方法
public void lock() {
sync.lock();
}
final void lock() {
if (compareAndSetState(0, 1)) //假设这里cas成功,state++.
//设置aqs加锁线程为线程1
setExclusiveOwnerThread(Thread.currentThread());
else
//这里是多线程抢锁才会触发.不分析了,有疑问去看ReentrantLock或者ReentrantReadWriteLock内部的分析
acquire(1);
}
如下图.
await方法
- 然后调用condition的await方法,释放线程1抢到的锁,并且加入到condition内部维护的一个等待队列里面
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
//获取或创建condition等待队列的当前线程创建的node节点.
Node node = addConditionWaiter();
int savedState = fullyRelease(node);
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
上面涉及到了好多方法.逐个分析
addConditionWaiter方法
private Node addConditionWaiter() {
Node t = lastWaiter;
// If lastWaiter is cancelled, clean out.
if (t != null && t.waitStatus != Node.CONDITION) {
unlinkCancelledWaiters();
t = lastWaiter;
}
//第一次调用await方法会新创建一个队列,专门用于存储被await的线程,
Node node = new Node(Thread.currentThread(), Node.CONDITION);
if (t == null)
firstWaiter = node;//firstWaiter第一个waiter指针指向node
else
t.nextWaiter = node;
lastWaiter = node; //最后一个waiter指针指向node
return node;//最后返回.
}
此时流程如下:创建一个线程1node—专属于condition阻塞队列. waitStatus为CONDITION类型.也就是-2
- 执行完addConditionWaiter方法之后,会接着走await剩下的方法
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
//获取或创建condition等待队列的当前线程创建的node节点.
Node node = addConditionWaiter();
int savedState = fullyRelease(node);//将线程1node传进去.不了解继续看下面的fullyRelease方法解析
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
fullyRelease方法
final int fullyRelease(Node node) {
boolean failed = true;
try {
int savedState = getState();//获取当前state的值.假设此时为1,只有线程1加了一次锁
if (release(savedState)) {
failed = false;
return savedState;
} else {
throw new IllegalMonitorStateException();
}
} finally {
if (failed)
node.waitStatus = Node.CANCELLED;
}
}
- 通过release(1)释放线程1加的锁.
release方法
public final boolean release(int arg) {//很熟悉了....在reentrantLock解析的时候释放锁的代码一模一样.
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
protected final boolean tryRelease(int releases) {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
int nextc = getState() - releases; //state-1 释放锁
boolean free = exclusiveCount(nextc) == 0;
if (free)
//清除线程1的加锁线程为null
setExclusiveOwnerThread(null);
setState(nextc);
return free;
}
此时流程图如下:
- fullyRelease方法会最终返回1.
- 执行完fullyRelease方法之后,会接着走await剩下的方法
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
//获取或创建condition等待队列的当前线程创建的node节点.
Node node = addConditionWaiter();
int savedState = fullyRelease(node);//将线程1node传进去.不了解继续看下面的fullyRelease方法解析
int interruptMode = 0;
while (!isOnSyncQueue(node)) {//isOnSyncQueue最终会返回false,进入while循环,阻塞住线程1
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
isOnSyncQueue方法
final boolean isOnSyncQueue(Node node) {
if (node.waitStatus == Node.CONDITION || node.prev == null)//满足条件,此时直接返回false
return false;
if (node.next != null) // If has successor, it must be on queue
return true;
return findNodeFromTail(node);
}
- 此时await最终 执行LockSupport.park(this);方法阻塞住线程1
await方法阻塞线程1完毕,线程2抢锁
- 当线程1被await方法阻塞住之后,线程2就可以通过lock方法进行加锁,加锁代码同上
流程图如下:线程2加速cas成功,将state加1,所占用线程更新为线程2自己
线程2通过signal方法主动释放线程1并cas抢占锁
signal方法
- signal方法同notify方法,都是唤醒一个线程的.我们看代码
public final void signal() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter; //获取condition队列的第一个节点,也就是线程1node
if (first != null)
doSignal(first);//进行唤醒
}
通过doSignal方法唤醒线程1
doSignal方法
private void doSignal(Node first) {
do {
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;//将firstWaiter和lastWaiter都置为null
} while (!transferForSignal(first) &&
(first = firstWaiter) != null);
}
transferForSignal方法
final boolean transferForSignal(Node node) {
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0)) //这里线程1进行cas将线程1状态改为0
return false;//直接返回.如果cas成功.会执行下面.
//执行enq方法从condition队列转移到AQD阻塞队列里面
Node p = enq(node);
int ws = p.waitStatus;
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}
enq方法
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
- 还是一步一图.首先将t指针指向tail 因为tail指针本身是null,所以在aqs内部创建一个虚拟阻塞节点.然后将tail指向head.如下图
- 然后继续循环,t指针指向tail也就是nodeHead,线程1node的prev指向t,也就是指向nodeHead ,然后设置node为tail尾节点.nodeHead的next指针指向node节点,然后返回nodeHead节点,如下图
- 我们接着看transferforSignal方法
final boolean transferForSignal(Node node) {
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0)) //这里线程1进行cas将线程1状态改为0
return false;//直接返回.如果cas成功.会执行下面.
//执行enq方法从condition队列转移到AQD阻塞队列里面,如上图
Node p = enq(node);
int ws = p.waitStatus;//更改nodehead虚拟节点的状态为-1
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);//释放线程1.
return true;
}
线程1被unpark后做了什么操作?
- 其实跟普通唤醒阻塞队列里面的线程类似.重回await方法
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter();
int savedState = fullyRelease(node);
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
LockSupport.park(this);//是在这里被唤醒的.
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
//这里
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
acquireQueued方法
- 这里的唤醒操作跟那个reentrantLock的唤醒一模一样.
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
- 首先获取线程1node的前驱节点也就是nodeHead ,第一个if假设线程1通过tryAcquire获取到了锁
tryAcquire内部会调用下面代码cas重试加锁.
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {//state线程++
setExclusiveOwnerThread(current);//将线程1设置为加锁线程
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;
}
如下图:将线程1node的内部线程清空
继续执行acquireQueued方法剩余逻辑
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
private void setHead(Node node) {
head = node; //头结点指向node
node.thread = null;//清空线程1node内部tread
node.prev = null;
}
- 将线程1node变为新的nodehead节点.原nodeHead会被垃圾回收掉.
总结:
- await方法会释放掉通过reentrantlock加的lock锁,同时让当前线程阻塞到 condition队列里面阻塞等待.直至被其他线程唤醒
- signal方法会被其他线程调用去释放调用被阻塞在condition队列的阻塞节点.转移到aqs对应的node阻塞链表里面,然后继续阻塞.
- 直到其他线程unlock掉自己占用的锁.就会去unpark中断 aqs内部的阻塞队列的线程阻塞.然后将其从阻塞队列中移除.