AQS相关同步组件的分析——Semaphore、CountDownLatch、CyclicBarrier

我们先说AQS,全程是AbstractQueueSynchronizer,顾名思义,抽象的队列式的同步器,AQS定义了一套多个线程访问共享资源的同步器框架,许多同步类实现都依赖于它,如ReentrantLock、Semaphore、CountDownLatch、CyclicBarrier等等。
这次我们重点说一下Semaphore、CountDownLatch、CyclicBarrier。首先看一下CountDownLatch,countDownLatch来保证所有线程最后同步计算某个任务,如果先执行的线程会等待其他线程,其中countDown()方法来执行减1操作,await()方法来执行等待操作。
下面我们用java代码来模拟多线程情况的使用countDownLatch来同步线程。

/**
 * countDownLatch来保证所有线程最后同步计算某个任务,先执行的线程会等待其他线程。
 */
@Slf4j
public class CountDownLatchExample1 {
    //一同并发执行的线程数量
    private final static int threadCount = 20;

    public static void main(String[] args) throws InterruptedException {
        //定义一个线程池
        ExecutorService exec = Executors.newCachedThreadPool();
        //定义一个闭锁
        final CountDownLatch countDownLatch = new CountDownLatch(threadCount);
        
        for (int i = 0; i < threadCount; i++) {
            final int threadNum = i;
            exec.execute(() -> {
                try {
                    test(threadNum);
                }catch (Exception e) {
                    log.error("exception",e);
                }finally {
                    countDownLatch.countDown();
                }
            });
        }
        countDownLatch.await();
        log.info("finish");
        exec.shutdown();

    }
    public static void test(int threadNum) throws Exception{
        Thread.sleep(100);
        log.info("{}",threadNum);
        Thread.sleep(100);
    }

}

我们来看一下打印结果
AQS相关同步组件的分析——Semaphore、CountDownLatch、CyclicBarrier_第1张图片我们可以看到当这20个线程都执行完毕后,最后才打印出finish,因为其中一个线程执行完test方法后走到await()方法,会等待其他线程,而且会先执行减1操作,最后为0,继续下一步的执行。

await()方法里也可以有参数,参数的含义为超时时间,就是说在这些时间里,能执行多少线程就执行多少,超出时间的线程就丢弃,不管了。
countDownLatch.await(10, TimeUnit.MILLISECONDS);//等待10ms,超过10ms后,将不会等待其他线程。

我们再看Semaphore,刚刚的countDownLatch是来保证线程同步执行,而Semaphore是来控制并发线程的数量,想让这一时刻3个线程并发则Semaphore中参数设为3,有3个资源。代码如下

/**
 *用semaphore来控制并发数量
 */
@Slf4j
public class SemaphoreExample1 {
    private final static int threadCount = 20;

    public static void main(String[] args) throws InterruptedException {
        //定义一个线程池
        ExecutorService exec = Executors.newCachedThreadPool();

        //定义一个信号量
        final Semaphore semaphore=new Semaphore(3);//允许的并发数

        for (int i = 0; i < threadCount; i++) {
            final int threadNum = i;
            exec.execute(() -> {
                try {
                    semaphore.acquire();//获得一个信号量资源
                    test(threadNum);
                    semaphore.release();//释放资源
                }catch (Exception e) {
                    log.error("exception",e);
                }
            });
        }
        log.info("finish");
        exec.shutdown();

    }
    public static void test(int threadNum) throws Exception{
        log.info("{}",threadNum);
        Thread.sleep(1000);
    }

}

执行结果是3个线程每隔1秒打印出来,大家可以再控制台测试查看效果。

最后我们来说一下CyclicBarrier,他和CountDownLatch类似,都是控制线程同步的,只不过CyclicBarrier是每个线程之间的互相等待,最后都执行好了进行下一步,CountDownLatch则是一个线程或多个线程等待一个线程执行完毕后,执行下一步。
我们直接看代码来区别他们

@Slf4j
public class CyclicBarrierExample1 {
    private static CyclicBarrier barrier = new CyclicBarrier(5);//5个线程要同步等待

    public static void main(String[] args) throws InterruptedException {


        ExecutorService exec = Executors.newCachedThreadPool();

        for(int i = 0; i < 10; i++) {
            final int threadNum = i;
            Thread.sleep(1000);
            exec.execute(() -> {
                try {
                    race(threadNum);
                }catch (Exception e) {
                    log.error("exception",e);
                }
            });
        }
    }
    public static void race(int threadNum) throws Exception{
        Thread.sleep(100);
        log.info("{} is ready",threadNum);
        barrier.await();
        log.info("{} is continue",threadNum);

    }
}

我们发现await()方法的位置并不一样,一个在动作体之内,一个在动作之外。
看一下打印结果
AQS相关同步组件的分析——Semaphore、CountDownLatch、CyclicBarrier_第2张图片因为我们给CyclicBarrier设置的参数为5,即5个线程才能共同进行下一步,发现打印出来的5个准备好之后,才进行下一步。
当然,CyclicBarrier和Semaphore也可以设置他们的超时时间,用法类似。

你可能感兴趣的:(多线程与高并发)