美文网首页
线程并发工具类之CountDownLatch

线程并发工具类之CountDownLatch

作者: 传达室马大爷 | 来源:发表于2020-04-06 13:54 被阅读0次

CountDownLatch

  • 一个线程等待另外多个线程完成后,在继续执行,有点类似于join()方法
  • CountdDwonLatch是通过计数器来实现的,初始化CountDownLatch实例时需要设置计数器的个数
  • CountdDwonLatch中主要用到两个方法,调用await()方法的线程处于阻塞状态,每调用一次countDown()方法,则计数器减1,直到计数器值为0时,await()方法所阻塞的线程会继续执行
  • 调用await()阻塞的线程可以不止一个,可以多个线程都调用await()进行阻塞,等满足条件时执行
实例
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadLocalRandom;

import com.shawntime.enjoy.architect.concurrency.SleepUtils;

/**
 * countDownLatch:
 */
public class CountDownLatchTest {

    public static void main(String[] args) {
        CountDownLatch countDownLatch = new CountDownLatch(6);

        for (int i = 0; i < 4; ++i) {
            InitThread initThread = new InitThread("init-thread-" + i, countDownLatch);
            initThread.start();
        }

        BusinessThread businessThread = new BusinessThread("business-thread", countDownLatch);
        businessThread.start();

        new Thread(() -> {
            {
                try {
                    SleepUtils.sleepBySeconds(1);
                    System.out.println(Thread.currentThread().getName() + " really init worker step 1...");
                } finally {
                    countDownLatch.countDown();
                }
                try {
                    SleepUtils.sleepBySeconds(1);
                    System.out.println(Thread.currentThread().getName() + " really init worker step 2...");
                } finally {
                    countDownLatch.countDown();
                }

            }
        }, "single-thread").start();

        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Main do work....");
    }


    /**
     * 初始化线程
     */
    private static class InitThread extends Thread {

        private CountDownLatch countDownLatch;

        public InitThread(String name, CountDownLatch countDownLatch) {
            super(name);
            this.countDownLatch = countDownLatch;
        }

        @Override
        public void run() {
            try {
                System.out.println(Thread.currentThread().getName() + " waiting really init...");
                int time = ThreadLocalRandom.current().nextInt(1000);
                SleepUtils.sleepByMilliSeconds(time);
            } finally {
                countDownLatch.countDown();
            }
            for (int i = 0; i < 3; ++i) {
                System.out.println(Thread.currentThread().getName() + " init continue working...");
            }
        }
    }

    /**
     * 业务线程
     */
    private static class BusinessThread extends Thread {

        private CountDownLatch countDownLatch;

        public BusinessThread(String name, CountDownLatch countDownLatch) {
            super(name);
            this.countDownLatch = countDownLatch;
        }

        @Override
        public void run() {
            try {
                countDownLatch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            for (int i = 0; i < 3; ++i) {
                System.out.println(Thread.currentThread().getName() + " business do working...");
            }
        }
    }
}

运行结果:
init-thread-1 waiting really init...
init-thread-0 waiting really init...
init-thread-2 waiting really init...
init-thread-3 waiting really init...
init-thread-0 init continue working...
init-thread-0 init continue working...
init-thread-0 init continue working...
init-thread-1 init continue working...
init-thread-1 init continue working...
init-thread-1 init continue working...
init-thread-2 init continue working...
init-thread-2 init continue working...
init-thread-2 init continue working...
init-thread-3 init continue working...
init-thread-3 init continue working...
init-thread-3 init continue working...
single-thread really init worker step 1...
single-thread really init worker step 2...
Main do work....
business-thread business do working...
business-thread business do working...
business-thread business do working...

结果分析

1、countDownLatch计数器个数为6,则必须等到调用了6次countDown()方法之后主线程和业务线程才会执行
2、主线程和业务线程同时调用了await()方法进行阻塞

countDownLatch()方法和join()方法的区别

  • join():调用某个thread的join()方法,当前线程会被阻塞,直到thread线程结束才会继续执行,join()的原理是不断检测thread线程是否存活,如果thread一直存活则会继续等待直到thread线程结束后notifyAll()方法被调用
实例:实现数据的初始化工作,主线程需要等待线程1、2、3均初始化完成后才能继续执行

countDownLatch()方法实现

import java.util.concurrent.CountDownLatch;

import com.shawntime.enjoy.architect.concurrency.SleepUtils;

public class DataInitForCountDownLatch {

    private static class InitThread extends Thread {

        private CountDownLatch countDownLatch;

        public InitThread(String name, CountDownLatch countDownLatch) {
            super(name);
            this.countDownLatch = countDownLatch;
        }

        @Override
        public void run() {
            try {
                SleepUtils.sleepByMilliSeconds(100);
                System.out.println(Thread.currentThread().getName() + "线程初始化第一步完成");

                SleepUtils.sleepByMilliSeconds(100);
                System.out.println(Thread.currentThread().getName() + "线程初始化第二步完成");
            } finally {
                countDownLatch.countDown();
            }
        }
    }

    public static void main(String[] args) {
        CountDownLatch countDownLatch = new CountDownLatch(3);
        for (int i = 1; i < 4; ++i) {
            new InitThread("thread-" + i, countDownLatch).start();
        }
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("主线程执行....");
    }
}

输出结果:
thread-3线程初始化第一步完成
thread-1线程初始化第一步完成
thread-2线程初始化第一步完成
thread-1线程初始化第二步完成
thread-2线程初始化第二步完成
thread-3线程初始化第二步完成
主线程执行....

join()方法实现

import com.shawntime.enjoy.architect.concurrency.SleepUtils;

public class DataInitForJoin {

    private static class InitThread extends Thread {

        public InitThread(String name) {
            super(name);
        }

        @Override
        public void run() {

            SleepUtils.sleepByMilliSeconds(100);
            System.out.println(Thread.currentThread().getName() + "线程初始化第一步完成");

            SleepUtils.sleepByMilliSeconds(100);
            System.out.println(Thread.currentThread().getName() + "线程初始化第二步完成");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        InitThread initThread1 = new InitThread("thread-1");
        InitThread initThread2 = new InitThread("thread-2");
        InitThread initThread3 = new InitThread("thread-3");

        initThread1.start();
        initThread2.start();
        initThread3.start();

        initThread1.join();
        initThread2.join();
        initThread3.join();

        System.out.println("主线程继续执行....");
    }
}

输出结果:
thread-2线程初始化第一步完成
thread-1线程初始化第一步完成
thread-3线程初始化第一步完成
thread-2线程初始化第二步完成
thread-1线程初始化第二步完成
thread-3线程初始化第二步完成
主线程继续执行....

如果我想在初始化第一步完成结束后主线程就可以继续执行的话,则join()方法无法实现,而countDownLatch则可以轻松的实现

import java.util.concurrent.CountDownLatch;

import com.shawntime.enjoy.architect.concurrency.SleepUtils;

public class DataInitForCountDownLatch {

    private static class InitThread extends Thread {

        private CountDownLatch countDownLatch;

        public InitThread(String name, CountDownLatch countDownLatch) {
            super(name);
            this.countDownLatch = countDownLatch;
        }

        @Override
        public void run() {
            try {
                SleepUtils.sleepByMilliSeconds(100);
                System.out.println(Thread.currentThread().getName() + "线程初始化第一步完成");
            } finally {
                countDownLatch.countDown();
            }
            SleepUtils.sleepByMilliSeconds(100);
            System.out.println(Thread.currentThread().getName() + "线程初始化第二步完成");
        }
    }

    public static void main(String[] args) {
        CountDownLatch countDownLatch = new CountDownLatch(3);
        for (int i = 1; i < 4; ++i) {
            new InitThread("thread-" + i, countDownLatch).start();
        }
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("主线程执行....");
    }
}

输出结果:
thread-1线程初始化第一步完成
thread-3线程初始化第一步完成
thread-2线程初始化第一步完成
主线程执行....
thread-1线程初始化第二步完成
thread-3线程初始化第二步完成
thread-2线程初始化第二步完成

总结:join()方法必须要等到线程结束后才能解除阻塞,而countDownLatch不需要等到线程结束,只要调用了countDown()方法保证计数器个数为0时即可解除阻塞,countDownLatch比join()方法更灵活

相关文章

网友评论

      本文标题:线程并发工具类之CountDownLatch

      本文链接:https://www.haomeiwen.com/subject/swxbphtx.html