java并发线程同步器CountDownLatch

java并发线程同步器CountDownLatch

  • CountDownLatch

CountDownLatch

我们在多线程编程时常常会遇到,主线程中启动多个子线程执行任务,并且主线程需要等待所有的子线程执行完成后在返回。这时我们可以使用CountDownLatch类来完成。

代码示例:

public static void countDownLatch(){
        CountDownLatch latch = new CountDownLatch(10);

        Thread[] threads = new Thread[10];
        for (int i = 0; i < threads.length; i++) {
            Thread thread = new Thread(new Runnable(){
                @Override
                public void run() {
                    try {
                        Thread.currentThread().sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("当前线程在执行:"+Thread.currentThread().getName());
                    latch.countDown();
                }
            });
            thread.setName("thread_"+i);
            threads[i] = thread;
        }

        System.out.println("开始启动所有的线程。。。。。。。。。。。");
        Arrays.stream(threads).forEach(Thread::start);
        System.out.println("线程启动完成:");
        try {
            latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("主线程返回的结果。。。。。。。。。。");

    }

执行结果:
java并发线程同步器CountDownLatch_第1张图片
解读:
上述代码中定义了一个长度为10的线程数组,创建10个子线程,然后分别将其启动。需要注意的是我们创建了CountDownLatch类来用作等这10个子线程全部执行完成后在将主线成中的打印输出在控制台。

我们在创建CountDownLatch时指定了其内部计数器的值为10,每一次一个子线程执行完自己的任务都会调用CountDownLatch.countDown()方法来讲计数器的值减1。我们在主线程中调用CountDownLatch.await()方法,该方法调用后会阻塞当前线程。直到CountDownLatch中内部计数器的值为0,或者其他子线程调用interrupt()方法中断了当前线程。才会停止阻塞。

CountDownLatch内部是使用AQS来实现计数操作的。
CountDownLatch.countDown()调用后,计数器的值递减,递减后如果计数器为0,则唤醒所有因调用await方法被阻塞的线程,否则什么都不做。countDown方法内部是使用CAS来递减计数器的。

CountDownLatch.await()被调用后将会阻塞当前线程,直到内部计数器为0或者其他线程调用interrupt方法中断当前线程,才会停止阻塞。

CountDownLatch.await(long timeout,TimeUnit unit)与CountDownLatch.await()类似只不过多增加了超时时间,当到达了超时时间会直接返回false当然也停止阻塞。

使用CountDownLatch.countDown()的好处是我们可以在子线程运行的任何时候调用。也就是未必我们要等到子线程全部执行完后返回,可能在某个关键的节点下去调用,也可以返回。
但是一旦CountDownLatch中的计数器为0时,在调用CountDownLatch.await()方法就不会在阻塞了。

最测试代码:

    public static void countDownLatch1(){
        CountDownLatch latch = new CountDownLatch(12);

        Thread[] threads = new Thread[12];
        for (int i = 0; i < threads.length; i++) {
            Thread thread = new Thread(()->{
                    try {
                        Thread.currentThread().sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("当前线程在执行:"+Thread.currentThread().getName());
                    latch.countDown();
            });
            thread.setName("thread_"+i);
            threads[i] = thread;
        }

        Thread thread1 = new Thread(()->{
                try {
                    latch.countDown();
                    System.out.println("进行测试线程阻塞1。。。。。。");
                    latch.await();
                    System.out.println("阻塞停止1。。。。。。。。。。");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        );
        thread1.setName("test_thread_1");
        Thread thread2 = new Thread(()->{
                try {
                    latch.countDown();
                    System.out.println("进行测试线程阻塞2。。。。。。");
                    latch.await();
                    System.out.println("阻塞停止2。。。。。。。。。。");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
        });
        thread2.setName("test_thread_2");

        System.out.println("开始启动所有的线程。。。。。。。。。。。");
        threads[10] = thread1;
        threads[11] = thread2;
        Arrays.stream(threads).forEach(Thread::start);
        System.out.println("线程启动完成:");
        try {
            latch.await();
            Thread.currentThread().sleep(1000);
            System.out.println("主线程返回的结果。。。。。。。。。。");

            latch.await();
            System.out.println("主线程返回的结果。。。。不等待直接返回。。。。");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

结果:
java并发线程同步器CountDownLatch_第2张图片

程序输出结果与我们的描述相同:
1,所有调用CountDownLatch.await()的线程都会被阻塞,也都会被同时释放
2,一旦CountDownLatch中的计数器为0时,在调用CountDownLatch.await()方法就不会在阻塞了

你可能感兴趣的:(java多线程编程,多线程,java,并发编程)