常用的线程计数器CountDownLatch与CyclicBarrier

版权声明:欢迎转载大宇的博客,转载请注明出处: https://blog.csdn.net/yanluandai1985/article/details/82387104

一、CountDownLatch类语法要点

        (1)构造函数接收计数器的值。

        (2)被计数的任务线程执行完毕后,记得调用一次 latch.countDown()方法,提示CountDownLatch对象,当前线程已经执行完毕。

        (3)在需要等待多个任务执行完毕后,在需要同步的地方,使用 latch.await()方法进行阻塞。再次恢复任务执行,需要CountDownLatch的计数器为0。

         缺点:CountDownLatch这个类的缺点就很明显,如果子线程耗时过多,那么主线程也会一直等待,程序执行效率大大降低。

         应用场景:

         开始执行任务前,等待 N 个前置线程完成各自的准备任务:例如应用程序启动类要确保在处理用户请求前,所有 N 个外部系统已经启动和运行了。

        有一种用法是new CountDownLatch(1),其它多个线程中调用 countDownLatch.await();在主函数中调用countDown(),然后其它阻塞的大量线程同时执行。

         另外用法示例:

        

import java.util.concurrent.CountDownLatch;

/**
 * Created by jay.zhou on 2018/9/4.
 */
public class CountDownLatchDemo {
    private static final int THREAD_COUNT_NUM = 7;
    private static CountDownLatch latch = new CountDownLatch(THREAD_COUNT_NUM);

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < THREAD_COUNT_NUM; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "执行");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //每个线程执行完毕,都把计数器减1
                latch.countDown();
            }, "Thread" + i).start();
        }

        //CountDownLatch.await()阻塞主函数。
        //那么,主函数需要等到 latch.countDown()被调用七次后,方可恢复执行
        latch.await();
        System.out.println("主函数阻塞结束");
        /**
         * Thread1执行
           Thread3执行
           Thread5执行
           Thread0执行
           Thread2执行
           Thread4执行
           Thread6执行
           主函数阻塞结束
         */

    }
}

        

二、CyclicBarrier类语法要点

         (1)CyclicBarrier 默认的构造方法是 CyclicBarrier(int parties),其参数表示屏障拦截的线程数量。

        (2)每个线程调用 CyclicBarrier.await() 方法告诉 CyclicBarrier 我已经到达了屏障,任务执行完毕,等待其它任务的完成。然后当前线程被阻塞。

        (3)线程之间相互等待。如果大家都到了,也就是所有线程都 await()了,那么将会执行CyclicBarrier的构造函数中执行的任务

package com.ssi.javaSE;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

/**
 * Created by jay.zhou on 2018/9/4.
 */
public class CyclicBarrierDemo {
    private static final int THREAD_COUNT_NUM = 4;
    public static void main(String[] args) {
        //创建一个Runnable接口实现
        Runnable task = () -> System.out.println("执行指定任务");
        //创建一个CyclicBarrier
        CyclicBarrier barrier = new CyclicBarrier(THREAD_COUNT_NUM , task);

        for (int i = 0; i < THREAD_COUNT_NUM; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "执行任务完毕");
                //我任务完成了,所以我等待了
                try {
                    barrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }, "Thread" + i).start();
        }

        /**
         * Thread0执行完毕
           Thread2执行完毕
           Thread1执行完毕
           Thread3执行完毕
           执行指定任务
         */
    }
}

三、使用场景

         为了提高一些查询数据库的性能,可以将一个查询分为多个线程去操作数据库。

         如果我们操作一个非常耗时的数据库操作的时候,例如一个查询,我需要把它分为多个线程分别去查询,等待所有的线程查询完之后,然后需要组装查询完的数据。

         例如,有一个查询条件集 List<Key>,目标是获取到Key对应的Value。所以,客户端希望得到的结果是Map<Key,Value>。

         思路:

         (1)先把List<Key>拆分成多个集合。  List<Key1> , List<Key2>,List<Key3>,满足 拆分的三个集合的size()等于List<Key>的size()。

         (2)创建一个CyclicBarrier对象,设置构造函数为3。

         (3)创建查询结果集,Map<Key,Value> map

        (4)同时开启三个线程,每个线程的任务就是根据 List<Key1>、List<Key2>、List<Key3>,查询出Map<Key1,Value1>、Map<Key2,Value2>、Map<Key3,Value3>。每个线程的任务执行完毕,调用CyclicBarrier对象的await()方法,等待其它线程执行完毕。

        (5)上述三个任务线程执行完毕之后,也就是准备任务都完成了。接着,CyclicBarrier执行构造函数中接收的Task。

               将任务执行的结果进行组装。将Map<Key1,Value1>、Map<Key2,Value2>、Map<Key3,Value3> 的值组装到 结果集 Map<Key,Value> map 中 ,完成最终的目标。

四、CyclicBarrier 和 CountDownLatch 的区别

         CountDownLatch 调用 await()方法后,会在此处阻塞。而CyclicBarrier不会阻塞线程,它只会在其它任务线程任务都执行完毕之后,执行构造函数中接收的任务。

         CountDownLatch 的计数器只能使用一次。而 CyclicBarrier 的计数器可以使用 reset() 方法重置。所以 CyclicBarrier 能处理更为复杂的业务场景,比如如果计算发生错误,可以重置计数器,并让线程们重新执行一次。

           

            

猜你喜欢

转载自blog.csdn.net/yanluandai1985/article/details/82387104