AbstractQueuedSynchronizer(十一)——应用之CountDownLatch

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阻塞的线程。




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值