1.1release
接下来我们在看看如何释放锁,源码如下
public final boolean release(int arg) {//释放锁方法(独占模式) if (tryRelease(arg)) {//尝试释放锁 Node h = head; if (h != null && h.waitStatus != 0)//如果head结点不为空并且等待状态不等于0就去唤醒后继结点 unparkSuccessor(h);//唤醒等待队列的下一个节点 return true; } return false; }
protected final boolean tryRelease(int releases) { int c = getState() - releases;//获取state值,释放给定的量 if (Thread.currentThread() != getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); boolean free = false; if (c == 0) {//如果c=0,表示锁已经释放 free = true; setExclusiveOwnerThread(null);//表示当前没有线程占用锁 } setState(c);//如果c不等于0,表示锁还没有完全释放,设置状态 return free; }
private void unparkSuccessor(Node node) {//唤醒后继节点
int ws = node.waitStatus;//获取给定节点的等待状态
if (ws < 0)//如果等待状态小于0,则置为0
compareAndSetWaitStatus(node, ws, 0);
Node s = node.next;//获取给定节点的下一个节点
if (s == null || s.waitStatus > 0) {//如果后继节点为null或者,等待状态>0
s = null;
//从后向前遍历队列,找到一个不是取消状态的节点 for (Node t = tail; t != null && t != node; t = t.prev) if (t.waitStatus <= 0) s = t; } if (s != null) LockSupport.unpark(s.thread);//唤醒 }
因此释放锁的过程可以总结为首先利用tryRelease尝试释放锁,如果失败,则返回false,否则调用unparkSuccessor方法去唤醒后继节点,成功则返回true。
1.2共享模式下获取锁
在前文中,我们讲了如果在独占模式获取锁和释放锁,接下来我们在看看在共享模式下如何获取锁和释放锁,在共享模式下获取锁的方式也是三种,分别是:不响应线程中断获取acquireShared,响应线程中断获取acquireSharedInterruptibly,设置超时时间获取tryAcquireSharedNanos。
1.2.1不响应线程中断获取
源码如下
public final void acquireShared(int arg) { if (tryAcquireShared(arg) < 0)//尝试获取锁 doAcquireShared(arg);//获取失败进入该方法 }
//尝试获取锁,由子类实现。1.负数表示获取失败;2.0表示获取成功,后继节点无法在获取;3.正数表示获取成功, //后继节点也可以获取成功 protected int tryAcquireShared(int arg) { throw new UnsupportedOperationException(); }
当获取锁失败之后,会调用doAcquireShared方法将当前线程构建成共享模式插入队列尾部,源码如下
private void doAcquireShared(int arg) { final Node node = addWaiter(Node.SHARED);//将当前线程包装成节点插入到队列尾部 boolean failed = true; try { boolean interrupted = false; for (;;) { final Node p = node.predecessor();//获取当前节点的前驱节点 if (p == head) { int r = tryAcquireShared(arg);//如果前驱节点为头节点,再次尝试获取锁 if (r >= 0) {//如果r>=0则表示获取锁成功 setHeadAndPropagate(node, r);//将自己设置为头节点,并 //且唤醒后面同样是共享模式的节点 p.next = null; // help GC if (interrupted) selfInterrupt();//如果在阻塞期间收到中断请求,则中断自己 failed = false; return; } } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; } } finally { if (failed) cancelAcquire(node); } }
private void setHeadAndPropagate(Node node, int propagate) { Node h = head; // Record old head for check below setHead(node);//将给定节点设置为头节点
if (propagate > 0 || h == null || h.waitStatus < 0 ||
(h = head) == null || h.waitStatus < 0) {
Node s = node.next;//获取给定节点的后继节点
if (s == null || s.isShared())
doReleaseShared();//唤醒后继节点
}
}
从上述代码可以看出,如果当前节点获取到锁,并且值大于0,则表示后面的节点也会获取到锁,因此当前节点就需要去唤醒后面同样是共享模式的结点,即使当前节点的后继节点为null,也会将等待状态设置为PROPAGATE来告诉后来的线程这个锁是可获取状态。
1.2.2响应线程中断获取
源码如下
public final void acquireSharedInterruptibly(int arg) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException();//判断线程是否中断,是抛出异常 if (tryAcquireShared(arg) < 0)//尝试获取锁 doAcquireSharedInterruptibly(arg);//获取失败进入该方法 }
private void doAcquireSharedInterruptibly(int arg) throws InterruptedException { final Node node = addWaiter(Node.SHARED); boolean failed = true; try { for (;;) { final Node p = node.predecessor(); if (p == head) { int r = tryAcquireShared(arg); if (r >= 0) { setHeadAndPropagate(node, r); p.next = null; // help GC failed = false; return; } } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) throw new InterruptedException();//线程在阻塞过程中收到中断请求,立马抛出异常 } } finally { if (failed) cancelAcquire(node); } }
1.2.3设置超时间获取
源码如下
public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException();//响应中断 return tryAcquireShared(arg) >= 0 || doAcquireSharedNanos(arg, nanosTimeout);//尝试获取锁,失败则调用doAcquireSharedNanos方法 }
private boolean doAcquireSharedNanos(int arg, long nanosTimeout) throws InterruptedException { if (nanosTimeout <= 0L) return false; final long deadline = System.nanoTime() + nanosTimeout; final Node node = addWaiter(Node.SHARED); boolean failed = true; try { for (;;) { final Node p = node.predecessor(); if (p == head) { int r = tryAcquireShared(arg); if (r >= 0) { setHeadAndPropagate(node, r); p.next = null; // help GC failed = false; return true; } } nanosTimeout = deadline - System.nanoTime(); if (nanosTimeout <= 0L) return false; if (shouldParkAfterFailedAcquire(p, node) && nanosTimeout > spinForTimeoutThreshold) LockSupport.parkNanos(this, nanosTimeout); if (Thread.interrupted()) throw new InterruptedException(); } } finally { if (failed) cancelAcquire(node); } }
如果第一次获取锁失败会调用doAcquireSharedNanos方法并传入超时时间,进入方法后会根据情况再次去获取锁,如果再次获取失败就要考虑将线程挂起了。这时会判断超时时间是否大于自旋时间,如果是的话就会将线程挂起一段时间,否则就继续尝试获取,每次获取锁之后都会将超时时间减去获取锁的时间,一直这样循环直到超时时间用尽,如果还没有获取到锁的话就会结束获取并返回获取失败标识。在整个期间线程是响应线程中断的。
1.3共享模式下释放锁
接下来我们再看看共享模式下是如何释放锁的,源码如下
public final boolean releaseShared(int arg) { if (tryReleaseShared(arg)) {//尝试释放锁 doReleaseShared();//成功则唤醒其他线程 return true; } return false; }
private void doReleaseShared() {
for (;;) {
Node h = head;//获取队列头节点
if (h != null && h != tail) {
int ws = h.waitStatus;//获取头节点的等待状态
if (ws == Node.SIGNAL) {//如果head结点的状态为SIGNAL, 表明后面有人在排队
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);//唤醒后继结点
}
//如果head结点的状态为0, 表明此时后面没人在排队, 就只是将head状态修改为PROPAGATE else if (ws == 0 && !compareAndSetWaitStatus(h, 0, Node.PROPAGATE)) continue; // loop on failed CAS } if (h == head) // loop if head changed break; } }
首先调用tryReleaseShared方法尝试释放锁,该方法的判断逻辑由子类实现。如果释放成功就调用doReleaseShared方法去唤醒后继结点。另外如果没有没有后继节点,那么将状态设置为PROPAGATE。