ConditionObject介绍(一)

1 ConditionObject的介绍&应用

像synchronized提供了wait和notify的方法实现线程在持有锁时,可以实现挂起,已经唤醒的操作。
ReentrantLock也拥有这个功能。
ReentrantLock提供了await和signal方法去实现类似wait和notify的功能。
想执行await或者是signal就必须先持有lock锁的资源。
先look一下Condition的应用

public static void main(String[] args) throws InterruptedException, IOException {
ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();
new Thread(() -> {
lock.lock();
System.out.println("子线程获取锁资源并await挂起线程");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
 }
System.out.println("子线程挂起后被唤醒!持有锁资源");
}).start();
Thread.sleep(100);
// =================main======================
lock.lock();
System.out.println("主线程等待5s拿到锁资源,子线程执行了await方法");
condition.signal();
System.out.println("主线程唤醒了await挂起的子线程");
lock.unlock();
}

2 Condition的构建方式&核心属性

发现在通过lock锁对象执行newCondition方法时,本质就是直接new的AQS提供的ConditionObject对象

final ConditionObject newCondition() {
return new ConditionObject();
}

其实lock锁中可以有多个Condition对象。
在对Condition1进行操作时,不会影响到Condition2的单向链表。
其次可以发现ConditionObject中,只有两个核心属性:

/** First node of condition queue. */
private transient Node firstWaiter;
/** Last node of condition queue. */
private transient Node lastWaiter;

虽然Node对象有prev和next,但是在ConditionObject中是不会使用这两个属性的,只要在Condition队列中,这两个属性都是null。在ConditionObject中只会使用nextWaiter的属性实现单向链表的效果。

3 Condition的await方法分析(前置分析)

持有锁的线程在执行await方法后会做几个操作:
● 判断线程是否中断,如果中断了,什么都不做。
● 没有中断,就讲当前线程封装为Node添加到Condition的单向链表中
● 一次性释放掉锁资源。
● 如果当前线程没有在AQS队列,就正常执行LockSupport.park(this)挂起线程。

// await方法的前置分析,只分析到线程挂起
public final void await() throws InterruptedException {
// 先判断线程的中断标记位是否是true
if (Thread.interrupted())
// 如果是true,就没必要执行后续操作挂起了。
throw new InterruptedException();
// 在线程挂起之前,先将当前线程封装为Node,并且添加到Condition队列中
Node node = addConditionWaiter();
// fullyRelease在释放锁资源,一次性将锁资源全部释放,并且保留重入的次数
int savedState = fullyRelease(node);
 // 省略一行代码……
// 当前Node是否在AQS队列中?
// 执行fullyRelease方法后,线程就释放锁资源了,如果线程刚刚释放锁资源,其他线程就立即执行了signal方法,
// 此时当前线程就被放到了AQS的队列中,这样一来线程就不需要执行LockSupport.park(this);去挂起线程了
while (!isOnSyncQueue(node)) {
// 如果没有在AQS队列中,正常在Condition单向链表里,正常挂起线程。
LockSupport.park(this);
// 省略部分代码……
}
// 省略部分代码……
}
// 线程挂起先,添加到Condition单向链表的业务~~
private Node addConditionWaiter() {
// 拿到尾节点。
Node t = lastWaiter;
// 如果尾节点有值,并且尾节点的状态不正常,不是-2,尾节点可能要拜拜了~
if (t != null && t.waitStatus != Node.CONDITION) {
// 如果尾节点已经取消了,需要干掉取消的尾节点~
unlinkCancelledWaiters();
// 重新获取lastWaiter
t = lastWaiter;
}
// 构建当前线程的Node,并且状态设置为-2
Node node = new Node(Thread.currentThread(), Node.CONDITION);
// 如果last节点为null。直接将当前节点设置为firstWaiter
if (t == null)
 firstWaiter = node;
else
// 如果last节点不为null,说明有值,就排在lastWaiter的后面
t.nextWaiter = node;
// 把当前节点设置为最后一个节点
lastWaiter = node;
// 返回当前节点
return node;
}
// 干掉取消的尾节点。
private void unlinkCancelledWaiters() {
// 拿到头节点
Node t = firstWaiter;
// 声明一个节点,爱啥啥~~~
Node trail = null;
// 如果t不为null,就正常执行~~
while (t != null) {
// 拿到t的next节点
Node next = t.nextWaiter;
// 如果t的状态不为-2,说明有问题
if (t.waitStatus != Node.CONDITION) {
// t节点的next为null
t.nextWaiter = null;
// 如果trail为null,代表头结点状态就是1,
if (trail == null)
// 将头结点指向next节点
 firstWaiter = next;
else
// 如果trail有值,说明不是头结点位置
trail.nextWaiter = next;
// 如果next为null,说明单向链表遍历到最后了,直接结束
if (next == null)
lastWaiter = trail;
}
// 如果t的状态是-2,一切正常
else {
// 临时存储t
trail = t;
}
// t指向之前的next
t = next;
}
}
// 一次性释放锁资源
final int fullyRelease(Node node) {
// 标记位,释放锁资源默认失败!
boolean failed = true;
try {
// 拿到现在state的值
int savedState = getState();
// 一次性释放干净全部锁资源
if (release(savedState)) {
 // 释放锁资源失败了么? 没有!
failed = false;
// 返回对应的锁资源信息
return savedState;
} else {
throw new IllegalMonitorStateException();
}
} finally {
if (failed)
// 如果释放锁资源失败,将节点状态设置为取消
node.waitStatus = Node.CANCELLED;
}
}

你可能感兴趣的:(java,开发语言)