zoukankan      html  css  js  c++  java
  • CountDownLatch同步计数器使用说明

    CountDownLatch概念

    CountDownLatch是一个同步工具类,用来协调多个线程之间的同步,或者说起到线程之间的通信(而不是用作互斥的作用)。

    CountDownLatch能够使一个线程在等待另外一些线程完成各自工作之后,再继续执行。使用一个计数器进行实现。计数器初始值为线程的数量。当每一个线程完成自己任务后,计数器的值就会减一。当计数器的值为0时,表示所有的线程都已经完成一些任务,然后在CountDownLatch上等待的线程就可以恢复执行接下来的任务。

    CountDownLatch工作原理

            CountDownLatch在多线程并发编程中充当一个计时器的功能,并且维护一个count的变量,并且其操作都是原子操作,该类主要通过countDown()和await()两个方法实现功能的,首先通过建立CountDownLatch对象,并且传入参数即为count初始值。如果一个线程调用了await()方法,那么这个线程便进入阻塞状态,并进入阻塞队列。如果一个线程调用了countDown()方法,则会使count-1;当count的值为0时,这时候阻塞队列中调用await()方法的线程便会逐个被唤醒,从而进入后续的操作。比如下面的例子就是有两个操作,一个是读操作一个是写操作,现在规定必须进行完写操作才能进行读操作。所以当最开始调用读操作时,需要用await()方法使其阻塞,当写操作结束时,则需要使count等于0。因此count的初始值可以定为写操作的记录数,这样便可以使得进行完写操作,然后进行读操作。

    首先是创建实例 CountDownLatch countDown = new CountDownLatch(2)
    需要同步的线程执行完之后,计数-1; countDown.countDown()
    需要等待其他线程执行完毕之后,再运行的线程,调用 countDown.await()实现阻塞同步

    CountDownLatch的用法

    CountDownLatch典型用法:1、某一线程在开始运行前等待n个线程执行完毕。将CountDownLatch的计数器初始化为new CountDownLatch(n),每当一个任务线程执行完毕,就将计数器减1 countdownLatch.countDown(),当计数器的值变为0时,在CountDownLatch上await()的线程就会被唤醒。一个典型应用场景就是启动一个服务时,主线程需要等待多个组件加载完毕,之后再继续执行。

    CountDownLatch典型用法:2、实现多个线程开始执行任务的最大并行性。注意是并行性,不是并发,强调的是多个线程在某一时刻同时开始执行。类似于赛跑,将多个线程放到起点,等待发令枪响,然后同时开跑。做法是初始化一个共享的CountDownLatch(1),将其计算器初始化为1,多个线程在开始执行任务前首先countdownlatch.await(),当主线程调用countDown()时,计数器变为0,多个线程同时被唤醒。

    CountDownLatch的不足

    CountDownLatch是一次性的,计算器的值只能在构造方法中初始化一次,之后没有任何机制再次对其设置值,当CountDownLatch使用完毕后,它不能再次被使用。

    CountDownLatch(倒计时计算器)使用说明

    方法说明

    public void countDown()

      递减锁存器的计数,如果计数到达零,则释放所有等待的线程。如果当前计数大于零,则将计数减少1.

    public boolean await() throws InterruptedException

    public boolean await(long timeout,TimeUnit unit) throws InterruptedException

      使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断或超出了指定的等待时间。如果当前计数为零,则此方法立刻返回true值。

      如果当前计数大于零,则出于线程调度目的,将禁用当前线程,且在发生以下三种情况之一前,该线程将一直出于休眠状态:

      由于调用countDown()方法,计数到达零;或者其他某个线程中断当前线程;或者已超出指定的等待时间。

    • 如果计数到达零,则该方法返回true值。
    • 如果当前线程,在进入此方法时已经设置了该线程的中断状态;或者在等待时被中断,则抛出InterruptedException,并且清除当前线程的已中断状态。
    • 如果超出了指定的等待时间,则返回值为false。如果该时间小于等于零,则该方法根本不会等待。

    参数:

      timeout-要等待的最长时间

      unit-timeout 参数的时间单位

    返回:

      如果计数到达零,则返回true;如果在计数到达零之前超过了等待时间,则返回false

    抛出:

      InterruptedException-如果当前线程在等待时被中断

     代码举例:

    package part2.tools.countdownlatch;
    
    import org.junit.Test;
    
    import java.util.concurrent.CountDownLatch;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    /**
     * @description:测试CountDownLatch。
     * CountDownLatch是一个同步工具类,用来协调多个线程之间的同步,或者说起到线程之间的通信(而不是用作互斥的作用)。
     * CountDownLatch能够使一个线程在等待另外一些线程完成各自工作之后,再继续执行。使用一个计数器进行实现。计数器初始值为线程的数量。
     * 当每一个线程完成自己任务后,计数器的值就会减一。当计数器的值为0时,表示所有的线程都已经完成一些任务,
     * 然后在CountDownLatch上等待的线程就可以恢复执行接下来的任务。
     */
    public class TestCountDownLatch {
        /**
         * 主线程等待子线程执行完成再执行
         */
        @Test
        public void test1() {
            int nThreads = 3;
            ExecutorService service = Executors.newFixedThreadPool(nThreads);
            final CountDownLatch latch = new CountDownLatch(nThreads);
            for (int i = 0; i < nThreads; i++) {
                Runnable runnable = new Runnable() {
                    @Override
                    public void run() {
                        try {
                            System.out.println("子线程" + Thread.currentThread().getName() + "开始执行");
                            Thread.sleep((long) (Math.random() * 10000));
                            System.out.println("子线程" + Thread.currentThread().getName() + "执行完成");
                            latch.countDown();//当前线程调用此方法,则计数减一
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                };
                service.execute(runnable);
            }
    
            try {
                System.out.println("主线程" + Thread.currentThread().getName() + "等待子线程执行完成...");
                latch.await();//阻塞当前线程,直到计数器的值为0
                System.out.println("主线程" + Thread.currentThread().getName() + "开始执行...");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 百米赛跑,4名运动员选手到达场地等待裁判口令,裁判一声口令,选手听到后同时起跑,当所有选手到达终点,裁判进行汇总排名
         */
        @Test
        public void test2() {
            ExecutorService service = Executors.newCachedThreadPool();
            final CountDownLatch cdOrder = new CountDownLatch(1);
            final CountDownLatch cdAnswer = new CountDownLatch(4);
            for (int i = 0; i < 4; i++) {
                Runnable runnable = new Runnable() {
                    @Override
                    public void run() {
                        try {
                            System.out.println("选手" + Thread.currentThread().getName() + "正在等待裁判发布口令");
                            cdOrder.await();
                            System.out.println("选手" + Thread.currentThread().getName() + "已接受裁判口令");
                            Thread.sleep((long) (Math.random() * 10000));
                            System.out.println("选手" + Thread.currentThread().getName() + "到达终点");
                            cdAnswer.countDown();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                };
                service.execute(runnable);
            }
            try {
                Thread.sleep((long) (Math.random() * 10000));
                System.out.println("裁判" + Thread.currentThread().getName() + "即将发布口令");
                cdOrder.countDown();
                System.out.println("裁判" + Thread.currentThread().getName() + "已发送口令,正在等待所有选手到达终点");
                cdAnswer.await();
                System.out.println("所有选手都到达终点");
                System.out.println("裁判" + Thread.currentThread().getName() + "汇总成绩排名");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            service.shutdown();
        }
    
    }

    运行结果:

    ##test1##
    主线程main等待子线程执行完成...
    子线程pool-1-thread-1开始执行
    子线程pool-1-thread-2开始执行
    子线程pool-1-thread-3开始执行
    子线程pool-1-thread-3执行完成
    子线程pool-1-thread-2执行完成
    子线程pool-1-thread-1执行完成
    主线程main开始执行...
    
    ##test2##
    选手pool-2-thread-1正在等待裁判发布口令
    选手pool-2-thread-2正在等待裁判发布口令
    选手pool-2-thread-3正在等待裁判发布口令
    选手pool-2-thread-4正在等待裁判发布口令
    裁判main即将发布口令
    裁判main已发送口令,正在等待所有选手到达终点
    选手pool-2-thread-1已接受裁判口令
    选手pool-2-thread-3已接受裁判口令
    选手pool-2-thread-2已接受裁判口令
    选手pool-2-thread-4已接受裁判口令
    选手pool-2-thread-3到达终点
    选手pool-2-thread-1到达终点
    选手pool-2-thread-2到达终点
    选手pool-2-thread-4到达终点
    所有选手都到达终点
    裁判main汇总成绩排名
  • 相关阅读:
    hdu 3342 Legal or Not 拓排序
    hdu 1596 find the safest road Dijkstra
    hdu 1874 畅通工程续 Dijkstra
    poj 2676 sudoku dfs
    poj 2251 BFS
    poj Prime Path BFS
    poj 3278 BFS
    poj 2387 Dijkstra 模板
    poj 3083 DFS 和BFS
    poj 1062 昂贵的聘礼 dijkstra
  • 原文地址:https://www.cnblogs.com/gongxr/p/14448514.html
Copyright © 2011-2022 走看看