java模板模式与AQS实现( 三 )

<= 0L)return false;final long deadline = System.nanoTime() + nanosTimeout;final Node node = addWaiter(Node.EXCLUSIVE);try {for (;;) {final Node p = node.predecessor();if (p == head && tryAcquire(arg)) {setHead(node);p.next = null; // help GCreturn true;}nanosTimeout = deadline - System.nanoTime();//核心if (nanosTimeout <= 0L) {cancelAcquire(node);return false;}if (shouldParkAfterFailedAcquire(p, node) &&nanosTimeout > SPIN_FOR_TIMEOUT_THRESHOLD)LockSupport.parkNanos(this, nanosTimeout);if (Thread.interrupted())throw new InterruptedException();}} catch (Throwable t) {cancelAcquire(node);throw t;}}
核心在于加入字段用于判断自旋状态是否超时,超时则取消请求,其他逻辑与独占类似 。
AQS在jdk中的应用之一:重入锁
重入锁,顾名思义,可以重新进入的锁,通俗来讲就是说,当一个线程获取到锁以后,该线程又请求同样的锁,可重入就是允许这种情况发生,不可重入就是不允许同一个线程请求同一个锁 。常见的锁基本都是可重入锁,比如关键字其实是可重入的 。java中有,即为基于AQS的可重入锁 。
分为公平锁和非公平锁,下边分别来看 。
首先可重入的前提是同步状态的意义,这时state字段不是资源数目,而是当前线程获得此资源的次数,当一个线程获取到此资源时,state加一,释放一次时减一,当state为0时说明没有线程占用此资源 。
非公平锁
所谓非公平锁,关键点在于当一个线程请求资源时先不必关心AQS中的同步队列,如下代码,当资源没有被占用时,当前线程直接执行CAS操作,如果成功,则此资源被当前线程占用,并把当前线程设为“owner ” 。如果state不是0时,先进行判断,当前线程是否是“owner ”,不是则不往下进行,返回失败,进行AQS的以及后续操作,这便实现了独占 。如果当前是“owner ”,则state加,实现计数(一般是+1),这里没有必要使用CAS,因为是单线程 。
随后可以执行释放资源,即进行state减,见下面代码,不在赘述 。
@ReservedStackAccessfinal boolean nonfairTryAcquire(int acquires) {final Thread current = Thread.currentThread();int c = getState();if (c == 0) {if (compareAndSetState(0, acquires)) {setExclusiveOwnerThread(current);return true;}}else if (current == getExclusiveOwnerThread()) {int nextc = c + acquires;if (nextc < 0) // overflowthrow new Error("Maximum lock count exceeded");setState(nextc);return true;}return false;}@ReservedStackAccessprotected final boolean tryRelease(int releases) {int c = getState() - releases;if (Thread.currentThread() != getExclusiveOwnerThread())throw new IllegalMonitorStateException();boolean free = false;if (c == 0) {free = true;setExclusiveOwnerThread(null);}setState(c);return free;}
公平锁
与非公平锁不同的地方在于获取资源部分,即,如下代码,比非公平多了一个s方法的判断,其余都一样 。
protected final boolean tryAcquire(int acquires) {final Thread current = Thread.currentThread();int c = getState();if (c == 0) {if (!hasQueuedPredecessors() &&compareAndSetState(0, acquires)) {setExclusiveOwnerThread(current);return true;}}else if (current == getExclusiveOwnerThread()) {int nextc = c + acquires;if (nextc < 0)throw new Error("Maximum lock count exceeded");setState(nextc);return true;}return false;}
继续看s这个方法干了什么:
public final boolean hasQueuedPredecessors() {Node h, s;if ((h = head) != null) {if ((s = h.next) == null || s.waitStatus > 0) {s = null; // traverse in case of concurrent cancellationfor (Node p = tail; p != h && p != null; p = p.prev) {if (p.waitStatus <= 0)s = p;}}if (s != null && s.thread != Thread.currentThread())return true;}return false;}