1.共享模式
之前说过共享模式下,如果队列中的节点获取锁之后,会通过setHeadAndPropagate触发后继节点的unpark操作,被唤醒的节点也会进行该操作,以此类推,只要tryAcquireShared成功,就会引发一些列的unpark操作,将之前所有因为没有获取到锁而park的线程都unpark掉。
CountDownLatch和guava的AbstractFuture都是类似的操作。本章以CountDownlatch来举例。
2.覆写的tryAcquireShared
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
判断状态是否为0,如果为0就可以获取到该锁。
状态的初始化时通过内部Sync的构造函数来设置的,
Sync(int count) {
setState(count);
}
我们在new CountDownLatch的时候会设置这个数值,代表the number of times {@link #countDown} must be invoked before threads can pass through {@link #await}。
tryAcquireShared会在acquireSharedInterruptibly调用,这里当执行await的时候会调用acquireSharedInterruptibly方法。
/**
* Causes the current thread to wait until the latch has counted down to
* zero, unless the thread is {@linkplain Thread#interrupt interrupted}.
*
* <p>If the current count is zero then this method returns immediately.
*
* <p>If the current count is greater than zero then the current
* thread becomes disabled for thread scheduling purposes and lies
* dormant until one of two things happen:
* <ul>
* <li>The count reaches zero due to invocations of the
* {@link #countDown} method; or
* <li>Some other thread {@linkplain Thread#interrupt interrupts}
* the current thread.
* </ul>
*
* <p>If the current thread:
* <ul>
* <li>has its interrupted status set on entry to this method; or
* <li>is {@linkplain Thread#interrupt interrupted} while waiting,
* </ul>
* then {@link InterruptedException} is thrown and the current thread's
* interrupted status is cleared.
*
* @throws InterruptedException if the current thread is interrupted
* while waiting
*/
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
同时还提供了带超时时间的await方法,对应的Sync的tryAcquireSharedNanos方法。
/**
* Causes the current thread to wait until the latch has counted down to
* zero, unless the thread is {@linkplain Thread#interrupt interrupted},
* or the specified waiting time elapses.
*
* <p>If the current count is zero then this method returns immediately
* with the value {@code true}.
*
* <p>If the current count is greater than zero then the current
* thread becomes disabled for thread scheduling purposes and lies
* dormant until one of three things happen:
* <ul>
* <li>The count reaches zero due to invocations of the
* {@link #countDown} method; or
* <li>Some other thread {@linkplain Thread#interrupt interrupts}
* the current thread; or
* <li>The specified waiting time elapses.
* </ul>
*
* <p>If the count reaches zero then the method returns with the
* value {@code true}.
*
* <p>If the current thread:
* <ul>
* <li>has its interrupted status set on entry to this method; or
* <li>is {@linkplain Thread#interrupt interrupted} while waiting,
* </ul>
* then {@link InterruptedException} is thrown and the current thread's
* interrupted status is cleared.
*
* <p>If the specified waiting time elapses then the value {@code false}
* is returned. If the time is less than or equal to zero, the method
* will not wait at all.
*
* @param timeout the maximum time to wait
* @param unit the time unit of the {@code timeout} argument
* @return {@code true} if the count reached zero and {@code false}
* if the waiting time elapsed before the count reached zero
* @throws InterruptedException if the current thread is interrupted
* while waiting
*/
public boolean await(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
3.覆写的tryReleaseShared
protected boolean tryReleaseShared(int releases) {
// Decrement count; signal when transition to zero
for (;;) {
int c = getState();
if (c == 0)
return false;
int nextc = c-1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
调用countdown的时候,会通过Sync的releaseShared方法调用tryReleaseShared,将状态减1。如果此时状态为0返回true,释放锁(该释放操作
会触发所有被await阻塞的线程的唤醒操作,one by one),If the new count is zero then
all waiting threads are re-enabled for thread scheduling purposes,这就是shared模式下setHeadAndPropagate的作用。
/**
* Decrements the count of the latch, releasing all waiting threads if
* the count reaches zero.
*
* <p>If the current count is greater than zero then it is decremented.
* If the new count is zero then all waiting threads are re-enabled for
* thread scheduling purposes.
*
* <p>If the current count equals zero then nothing happens.
*/
public void countDown() {
sync.releaseShared(1);
}
4.一次性的操作
对于CountDownLatch的操作是一次性的,因为这里只提供了一个和修改底层状态有关的方法——countDown,将状态减一,所以当一个CountDownLatch的状态被CountDown到0,这个CountDownLatch就已经没用了,就和Thread一样,当run方法执行过之后,该线程就没用了一个道理。5.状态
对于应用AbstractQueuedSynchronizer做二次开发的时候,最重要的就是状态和覆写的一组tryAcquire/tryRelease方法(tryAcquireShared/tryReleaseShared),无论获取锁还是释放锁,和锁相关的操作都和状态有关。在这里CountDownLatch获取锁就是判断状态是否为0,而释放锁就是修改状态。ReetrantLock的获取锁和释放锁也涉及对状态的判断和修改。
和状态有关的三个操作,getState,setState和compareAndSetState在AbstractQueuedSynchronizer中基本没有调用,都是在做二次开发的时候调用。
总结:获取锁和释放锁的关键就是对状态的读取和修改。
6.扩展countDownAll
提供一个countDownAll的方法。使用场景,比如一个工序需要5步,这5步在不同的线程中执行,只有这5个步骤都执行完,被该CountDownLatch阻塞的线程才能继续向下执行。同时还有一个特殊工序,只要这个工序完成了,不管还有几个工序没有被完成,被该CountDownLatch阻塞的线程都可以继续向下执行。
之前说过,关于锁操作的功能的实现都依赖于对状态的读取和修改,这个功能也不例外,修改tryReleasedShared方法,
/**
* times of countdown depends on releases, if state is negative
* after countdown, assign zero to it to keep state positive
*
* @param releases negative:count down all, else count down one
* positive:count down number of releases
*/
protected boolean tryReleaseShared(int releases) {
// Decrement count; signal when transition to zero
for (; ; ) {
int c = getState();
if (c == 0)
return false;
// clear state
if (releases < 0) {
// if (compareAndSetState(c, 0))
/**
* here CAS is unnecessary, no matter if other thread is changing or has
* changed the state, {@link #countDownAll()} will reset the state.
*/
setState(0);
return true;
} else {
int nextc = c - releases;
// if after this countdown, state is less than 0, assign 0 to it
// to keep state positive
if (nextc < 0 && compareAndSetState(c, 0)) {
nextc = 0;
}
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
}
public void countDownAll() {
sync.releaseShared(-1);
}
执行countDownAll的时候,调用releaseShared,参数为-1,releaseShared方法中判断参数为-1的时候,直接重置状态为0,并返回true,释放所有被await阻塞的线程。