重学多线程(七)—— 并发框架类

前言

java.concurrent包提供了几个非常实用的并发工具类,这些工具类提供了一种非常有效的并发流程控制手段。

CountDownLatch

CountDownLatch 允许一个或多个线程等待其他线程完成操作。CountDownLatch的具体实现如下:

public class CountDownLatch {

    private static final class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 4982264981922014374L;

        Sync(int count) {
            setState(count);
        }

        int getCount() {
            return getState();
        }

        protected int tryAcquireShared(int acquires) {
            return (getState() == 0) ? 1 : -1;
        }

        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;
            }
        }
    }

    private final Sync sync;

    public CountDownLatch(int count) {
        if (count < 0) throw new IllegalArgumentException("count < 0");
        this.sync = new Sync(count);
    }

    public void await() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }

    public boolean await(long timeout, TimeUnit unit)
        throws InterruptedException {
        return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
    }

    public void countDown() {
        sync.releaseShared(1);
    }

    public long getCount() {
        return sync.getCount();
    }

    public String toString() {
        return super.toString() + "[Count = " + sync.getCount() + "]";
    }
}

从内部构造上看,CountDownLatch内部含有一个继承自AbstractQueuedSynchronizer(AQS,队列同步器)的内部类Sync,关于AQS的内容,博主在《重学多线程(三)—— 锁》 一文中有比较详细的介绍。

CountDownLatch的构造函数接收一个int类型的参数作为计数器,表示等待的线程数量。当调用countDown方法时,计数器就会减1,await方法会阻塞当前线程,直至计数器变为0。当计数器为0时,调用await方法不会阻塞当前线程,CountDownLatch不可能重新初始化或者修改计数器的值,说白了就是不能复用。

CyclicBarrier

CyclicBarrier 允许一组线程到达一个 barrier 时被阻塞,直到最后一个线程到达 barrier 时,barrier才会打开,被拦截的线程继续允许。

     public CyclicBarrier(int parties, Runnable barrierAction) {
        if (parties <= 0) throw new IllegalArgumentException();
        this.parties = parties;
        this.count = parties;
        this.barrierCommand = barrierAction;
    }

    public CyclicBarrier(int parties) {
        this(parties, null);
    }

CyclicBarrier 的默认构造方法接受一个 int 类型的参数,表示 barrier 拦截的线程数量。同时,也可以通过 barrierAction 参数指定当所有线程到达barrier后,优先执行的动作, 以方便处理更复杂的业务。
CyclicBarrier 最重要的成员方法便是await方法:

    public int await() throws InterruptedException, BrokenBarrierException {
        try {
            return dowait(false, 0L);
        } catch (TimeoutException toe) {
            throw new Error(toe); // cannot happen
        }
    }

    private int dowait(boolean timed, long nanos)
        throws InterruptedException, BrokenBarrierException,
               TimeoutException {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            final Generation g = generation;

            if (g.broken)
                throw new BrokenBarrierException();

            if (Thread.interrupted()) {
                breakBarrier();
                throw new InterruptedException();
            }

            int index = --count;
            if (index == 0) {  // tripped
                boolean ranAction = false;
                try {
                    final Runnable command = barrierCommand;
                    if (command != null)
                        command.run();
                    ranAction = true;
                    nextGeneration();
                    return 0;
                } finally {
                    if (!ranAction)
                        breakBarrier();
                }
            }

            // loop until tripped, broken, interrupted, or timed out
            for (;;) {
                try {
                    if (!timed)
                        trip.await();
                    else if (nanos > 0L)
                        nanos = trip.awaitNanos(nanos);
                } catch (InterruptedException ie) {
                    if (g == generation && ! g.broken) {
                        breakBarrier();
                        throw ie;
                    } else {
                        // We're about to finish waiting even if we had not
                        // been interrupted, so this interrupt is deemed to
                        // "belong" to subsequent execution.
                        Thread.currentThread().interrupt();
                    }
                }

                if (g.broken)
                    throw new BrokenBarrierException();

                if (g != generation)
                    return index;

                if (timed && nanos <= 0L) {
                    breakBarrier();
                    throw new TimeoutException();
                }
            }
        } finally {
            lock.unlock();
        }
    }

可以看到,当调用await方法时,先对计数器 count 自减操作,判断 count 值是否为0,如果不为零,则调用 Condition的await方法使当前线程进入等待状态,当最后一个线程进入临界区对 count 值自减操作后,此时 count 值为0,便调用Condition的 signalAll 方法唤醒一个等待的线程,而被唤醒的线程在退出临界区后再次调用Condition的 signalAll 方法唤醒一个等待线程,通过线程间的循环唤醒,使得所有线程再次进入运行状态。

CountDownLatch的计数器只能使用一次,而CyclicBarrier 的计数器可以通过调用 reset 方法重置,这是两者比较大的区别。

Semaphore

Semaphore(信号量)是用来控制同时访问特定资源的线程数量,通过协调各个线程,以保证合理的使用公共资源。

    abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 1192457210091910933L;

        Sync(int permits) {
            setState(permits);
        }

        final int getPermits() {
            return getState();
        }

        final int nonfairTryAcquireShared(int acquires) {
            for (;;) {
                int available = getState();
                int remaining = available - acquires;
                if (remaining < 0 ||
                    compareAndSetState(available, remaining))
                    return remaining;
            }
        }

        protected final boolean tryReleaseShared(int releases) {
            for (;;) {
                int current = getState();
                int next = current + releases;
                if (next < current) // overflow
                    throw new Error("Maximum permit count exceeded");
                if (compareAndSetState(current, next))
                    return true;
            }
        }

        final void reducePermits(int reductions) {
            for (;;) {
                int current = getState();
                int next = current - reductions;
                if (next > current) // underflow
                    throw new Error("Permit count underflow");
                if (compareAndSetState(current, next))
                    return;
            }
        }

        final int drainPermits() {
            for (;;) {
                int current = getState();
                if (current == 0 || compareAndSetState(current, 0))
                    return current;
            }
        }
    }

    public Semaphore(int permits) {
        sync = new NonfairSync(permits);
    }

    public Semaphore(int permits, boolean fair) {
        sync = fair ? new FairSync(permits) : new NonfairSync(permits);
    }

跟 CountDownLatch 一样,Semaphore 内部也含有一个继承自AbstractQueuedSynchronizer(AQS,队列同步器)的内部类Sync。Semaphore 的构造方法接受一个 int 类型参数,表示可用的许可证数量,而参数 fair 表示获取许可证的时候是否公平。可以调用Semaphore的 acquire 方法获取一个许可证,调用 release 方法归还许可证。

顺便说一下,Google的Guava框架中有个RateLimiter的工具类也提供了限流的功能,读者有兴趣可以自行了解一下。

总结

本文介绍的三个并发工具类的使用方法,在实际开发过程中如果有类似场景,不妨一试。

猜你喜欢

转载自blog.csdn.net/tjreal/article/details/80035542