11

倒计时器CountDownLatch

 4 years ago
source link: http://www.cnblogs.com/tigerBoy/p/countDownLatch.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

1.背景:

  • countDownLatch是在java1.5被引入,跟它一起被引入的工具类还有CyclicBarrier、Semaphore、concurrentHashMap和BlockingQueue。
  • 存在于java.util.cucurrent包下。

2.概念

  • countDownLatch这个类使一个线程等待其他线程各自执行完毕后再执行。
  • 是通过一个计数器来实现的,计数器的初始值是线程的数量。每当一个线程执行完毕后,计数器的值就-1,当计数器的值为0时,表示所有线程都执行完毕,然后在闭锁上等待的线程就可以恢复工作了。

3.使用场景

example1

为了能够理解CountDownLatch,举一个很通俗的例子,运动员进行跑步比赛时,假设有6个运动员参与比赛,裁判员在终点会为这6个运动员分别计时,

可以想象每当一个运动员到达终点的时候,对于裁判员来说就少了一个计时任务。直到所有运动员都到达终点了,裁判员的任务也才完成。这6个运动员可

以类比成6个线程,当线程调用CountDownLatch.countDown方法时就会对计数器的值减一,直到计数器的值为0的时候,裁判员(调用await方法的线程)

才能继续往下执行。

下面来看些CountDownLatch的一些重要方法。

先从CountDownLatch的构造方法看起:

public CountDownLatch(int count)

构造方法会传入一个整型数N,之后调用CountDownLatch的 countDown 方法会对N减一,知道N减到0的时候,当前调用 await 方法的线程继续执行。

CountDownLatch的方法不是很多,将它们一个个列举出来:

1. await() throws InterruptedException:调用该方法的线程等到构造方法传入的N减到0的时候,才能继续往下执行;

2. await(long timeout, TimeUnit unit):与上面的await方法功能一致,只不过这里有了时间限制,调用该方法的线程等到指定的timeout时间后,不管N是

否减至为0,都会继续往下执行;

3. countDown():使CountDownLatch初始值N减1;

4. long getCount():获取当前CountDownLatch维护的值;

下面用一个具体的例子来说明CountDownLatch的具体用法:

package com.example.demo.test;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
  * 
  * @version V1.0 15:44 guozl
  * @since 1.0
  */
public class CountDownLatchDemo {
    private static CountDownLatch startSignal = new CountDownLatch(1);
    //用来表示裁判员需要维护的是6个运动员
    private static CountDownLatch endSignal = new CountDownLatch(6);
    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(6);
        for (int i = 0; i < 6; i++) {
            executorService.execute(() -> {
                try {
                    System.out.println(Thread.currentThread().getName() + " 运动员就位等待裁判员响哨!!!");
                    startSignal.await();
                    System.out.println(Thread.currentThread().getName() + "正在全力冲刺");
                    System.out.println(Thread.currentThread().getName() + " 到达终点");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    endSignal.countDown();
                }
            });
        }
        Thread.sleep(1000);
        System.out.println("裁判员发号施令啦!!!");
        startSignal.countDown();
        endSignal.await();
        System.out.println("所有运动员到达终点,比赛结束!");
        executorService.shutdown();

    }
}


输出结果:
pool-1-thread-2 运动员就位等待裁判员响哨!!!
pool-1-thread-1 运动员就位等待裁判员响哨!!!
pool-1-thread-4 运动员就位等待裁判员响哨!!!
pool-1-thread-3 运动员就位等待裁判员响哨!!!
pool-1-thread-5 运动员就位等待裁判员响哨!!!
pool-1-thread-6 运动员就位等待裁判员响哨!!!
裁判员发号施令啦!!!
pool-1-thread-2正在全力冲刺
pool-1-thread-2 到达终点
pool-1-thread-3正在全力冲刺
pool-1-thread-3 到达终点
pool-1-thread-4正在全力冲刺
pool-1-thread-4 到达终点
pool-1-thread-5正在全力冲刺
pool-1-thread-5 到达终点
pool-1-thread-1正在全力冲刺
pool-1-thread-1 到达终点
pool-1-thread-6正在全力冲刺
pool-1-thread-6 到达终点
所有运动员到达终点,比赛结束!


该示例代码中设置了两个CountDownLatch,第一个 endSignal 用于控制让main线程(裁判员)必须等到其他线程(运动员)让CountDownLatch维护的

数值N减到0为止。另一个 startSignal 用于让main线程对其他线程进行“发号施令”,startSignal引用的CountDownLatch初始值为1,而其他线程执行的

run方法中都会先通过 startSignal.await() 让这些线程都被阻塞,直到main线程通过调用 startSignal.countDown(); ,将值N减

1,CountDownLatch维护的数值N为0后,其他线程才能往下执行,并且,每个线程执行的run方法中都会通

过 endSignal.countDown(); 对 endSignal 维护的数值进行减一,由于往线程池提交了6个任务,会被减6次,所以 endSignal 维护的值最终会变为0,

因此main线程在 latch.await(); 阻塞结束,才能继续往下执行。

另外,需要注意的是,当调用CountDownLatch的countDown方法时,当前线程是不会被阻塞,会继续往下执行,比如在该例中会继续输出 pool-1-

thread-4 到达终点 。

example2

模拟数据量较大 多线程分页查询处理

package com.example.demo.test;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * CountDownLatch 的使用示例
 * @version V1.0 10:51 guozl
 * @since 1.0
 */
public class CountDownLatchExample {
    // 请求的数量(每页数量)
    private static final int threadCount = 8;

    public static void main(String[] args) throws InterruptedException {
        //定义j为页数
        for (int j = 1; j < 3; j++) {
            ExecutorService threadPool = Executors.newFixedThreadPool(5);
            final CountDownLatch countDownLatch = new CountDownLatch(threadCount);
            for (int i = 0; i < threadCount; i++) {
                final int threadnum = i;
                threadPool.execute(() -> {// Lambda 表达式的运用
                    try {
                        //实际业务操作
                        test(threadnum);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        countDownLatch.countDown();// 表示一个请求已经被完成
                    }
                });
            }
            countDownLatch.await();
            threadPool.shutdown();
            System.out.println("finish");
        }
        System.out.println("all finish");
        System.out.println("处理所有数据业务操作");
    }

    public static void test(int threadnum) throws InterruptedException {
        Thread.sleep(500);// 模拟请求的耗时操作
        System.out.println("threadnum:" + threadnum);
        Thread.sleep(500);// 模拟请求的耗时操作
    }
}

执行结果:

threadnum:4
threadnum:2
threadnum:3
threadnum:0
threadnum:1
threadnum:7
threadnum:6
threadnum:5
finish
threadnum:2
threadnum:4
threadnum:0
threadnum:1
threadnum:3
threadnum:7
threadnum:6
threadnum:5
finish
all finish
处理所有数据业务操作
 

下一篇分享循环栅栏:CyclicBarrier  并与CountDownLatch做比较  有问题欢迎评论区交流 共同进步。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK