独占释放资源( release(int i) )
此方法是acquire()的反操作,是独占模式下线程释放共享资源的入口。他会释放指定资源,如果彻底释放则 state = 0 ,他会唤醒等待队列里的其他线程来获取资源。这就是解锁的意义。下面是release()源码。
为了更好理解我再次重复下上一章的知识点,AQS在判断状态时,通过用waitStatus>0表示取消状态,而waitStatus<0表示有效状态。
public final boolean release(int arg) {
// 尝试释放资源
if (tryRelease(arg)) {
Node h = head;
// 头结点存在 且头结点状态不为0
// 如果头结点不为初始化状态则唤醒队列下一个等待的线程
if (h != null && h.waitStatus != 0)
// 唤醒线程
unparkSuccessor(h);
return true;
}
return false;
}
尝试释放资源tryRelease()
和tryAcquire()一样,这个方法时需要独占模式自定义同步器去实现的。如果是独占模式下该线程释放资源说明这个线程已经拿到资源了。直接减掉相应的资源状态即可,也不需要考虑线程安全的问题。但是release是通过tryRelease来判断是否释放过资源的,所以如果已经释放资源则应当返回true,否则返回false。
在上上章的EasyLock中释放实现如下。
@Override
protected boolean tryRelease(int arg) {
// 将当前线程清空
setExclusiveOwnerThread(null);
//将states设置为0
setState(0);
// 返回成功
return true;
}
尝试唤醒后继线程unparkSuccessor()
此方法用于当前线程执行完毕后唤醒后继线程来获取资源。
private void unparkSuccessor(Node node) {
// node为当前线程所在的结点。
int ws = node.waitStatus;
// 该节点状态是有效的 就将其设置为初始化状态
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
// 找到下一个需要唤醒的结点
Node s = node.next;
//如果为空或已取消
if (s == null || s.waitStatus > 0) {
s = null;
// 从尾节点往前找。 找到有效或者初始化的点,直到找到最前面的不是node的点。
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
// 如果找的到 则唤醒该节点.
if (s != null)
LockSupport.unpark(s.thread);
}
也就是说 用unpark唤醒等待队列中最前的没有放弃的不是当前节点的节点线程
至此在独占模式下的解锁和上锁功能就将讲解完成了。
网友评论