Java主线程等待所有子线程执行完毕再执行的几种常见方法

in TCEHJava with 0 comment

一、sleep()方法

  即通过 Thread.sleep() 方法,来等待子线程执行完毕。这种方式明显有bug,因为不确定子线程执行耗时。

二、join()方法

  即通过 thread.join() 方法,来等待子线程执行完毕。且该方法会把指定的线程加入到当前线程,可以将多个交替执行的线程合并为顺序执行的线程。

package com.example.demo;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.TimeUnit;

@Slf4j
public class ThreadTest {
    public static void main(String[] args) throws InterruptedException {
        log.info("开始执行主线程...");
        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(() -> {
                log.info("开始执行子线程...等待1秒钟");
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
            thread.start();
            thread.join();
        }
        log.info("恢复执行主线程...");
    }
}
/**
 * 以上代码执行结果
 * 20:21:54.073 [main] INFO com.example.demo.ThreadTest - 开始执行主线程...
 * 20:21:54.219 [Thread-0] INFO com.example.demo.ThreadTest - 开始执行子线程...等待1秒钟
 * 20:21:55.222 [Thread-1] INFO com.example.demo.ThreadTest - 开始执行子线程...等待1秒钟
 * 20:21:56.223 [Thread-2] INFO com.example.demo.ThreadTest - 开始执行子线程...等待1秒钟
 * 20:21:57.225 [Thread-3] INFO com.example.demo.ThreadTest - 开始执行子线程...等待1秒钟
 * 20:21:58.227 [Thread-4] INFO com.example.demo.ThreadTest - 开始执行子线程...等待1秒钟
 * 20:21:59.229 [Thread-5] INFO com.example.demo.ThreadTest - 开始执行子线程...等待1秒钟
 * 20:22:00.232 [Thread-6] INFO com.example.demo.ThreadTest - 开始执行子线程...等待1秒钟
 * 20:22:01.235 [Thread-7] INFO com.example.demo.ThreadTest - 开始执行子线程...等待1秒钟
 * 20:22:02.239 [Thread-8] INFO com.example.demo.ThreadTest - 开始执行子线程...等待1秒钟
 * 20:22:03.242 [Thread-9] INFO com.example.demo.ThreadTest - 开始执行子线程...等待1秒钟
 * 20:22:04.242 [main] INFO com.example.demo.ThreadTest - 恢复执行主线程...
 */

三、CountDownLatch

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

package com.example.demo;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

@Slf4j
public class ThreadTest {
    public static void main(String[] args) throws InterruptedException {
        log.info("开始执行主线程...");
        CountDownLatch countDownLatch = new CountDownLatch(10);
        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(() -> {
                log.info("开始执行子线程...等待1秒钟");
                try {
                    TimeUnit.SECONDS.sleep(1);
                    countDownLatch.countDown();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
            thread.start();
        }
        countDownLatch.await();
        log.info("恢复执行主线程...");
    }
}
/**
 * 以上代码执行结果
 * 20:27:25.516 [main] INFO com.example.demo.ThreadTest - 开始执行主线程...
 * 20:27:25.647 [Thread-0] INFO com.example.demo.ThreadTest - 开始执行子线程...等待1秒钟
 * 20:27:25.647 [Thread-1] INFO com.example.demo.ThreadTest - 开始执行子线程...等待1秒钟
 * 20:27:25.648 [Thread-3] INFO com.example.demo.ThreadTest - 开始执行子线程...等待1秒钟
 * 20:27:25.649 [Thread-4] INFO com.example.demo.ThreadTest - 开始执行子线程...等待1秒钟
 * 20:27:25.649 [Thread-5] INFO com.example.demo.ThreadTest - 开始执行子线程...等待1秒钟
 * 20:27:25.649 [Thread-6] INFO com.example.demo.ThreadTest - 开始执行子线程...等待1秒钟
 * 20:27:25.650 [Thread-8] INFO com.example.demo.ThreadTest - 开始执行子线程...等待1秒钟
 * 20:27:25.650 [Thread-7] INFO com.example.demo.ThreadTest - 开始执行子线程...等待1秒钟
 * 20:27:25.651 [Thread-9] INFO com.example.demo.ThreadTest - 开始执行子线程...等待1秒钟
 * 20:27:25.655 [Thread-2] INFO com.example.demo.ThreadTest - 开始执行子线程...等待1秒钟
 * 20:27:26.657 [main] INFO com.example.demo.ThreadTest - 恢复执行主线程...
 */

四、CyclicBarrier

  与 CountDownLatch 类颇为相似。CyclicBarrier字面意思是“可重复使用的栅栏”。CyclicBarrier 的源码实现和 CountDownLatch 大相径庭,CountDownLatch 基于 AQS 的共享模式的使用,而 CyclicBarrier 基于 Condition 来实现的。在CyclicBarrier类的内部同样有一个计数器,每个线程在到达屏障点的时候都会调用await方法将自己阻塞,此时计数器会减一,当计数器的值为0时,所有因调用await方法而被阻塞的线程将被唤醒。

package com.example.demo;

import lombok.extern.slf4j.Slf4j;

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

@Slf4j
public class ThreadTest {
    public static void main(String[] args) throws InterruptedException {
        log.info("开始执行主线程...");
        CyclicBarrier cyclicBarrier = new CyclicBarrier(10);
        for (int i = 0; i < 9; i++) {
            Thread thread = new Thread(() -> {
                try {
                    log.info("开始执行子线程...等待1秒钟");
                    TimeUnit.SECONDS.sleep(1);
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            });
            thread.start();

        }
        try {
            cyclicBarrier.await();
        } catch (BrokenBarrierException e) {
            e.printStackTrace();
        }
        log.info("恢复执行主线程...");
    }
}
/**
 * 以上代码执行结果
 * 20:50:33.296 [main] INFO com.example.demo.ThreadTest - 开始执行主线程...
 * 20:50:33.427 [Thread-2] INFO com.example.demo.ThreadTest - 开始执行子线程...等待1秒钟
 * 20:50:33.427 [Thread-1] INFO com.example.demo.ThreadTest - 开始执行子线程...等待1秒钟
 * 20:50:33.428 [Thread-0] INFO com.example.demo.ThreadTest - 开始执行子线程...等待1秒钟
 * 20:50:33.429 [Thread-6] INFO com.example.demo.ThreadTest - 开始执行子线程...等待1秒钟
 * 20:50:33.430 [Thread-5] INFO com.example.demo.ThreadTest - 开始执行子线程...等待1秒钟
 * 20:50:33.429 [Thread-3] INFO com.example.demo.ThreadTest - 开始执行子线程...等待1秒钟
 * 20:50:33.430 [Thread-7] INFO com.example.demo.ThreadTest - 开始执行子线程...等待1秒钟
 * 20:50:33.431 [Thread-8] INFO com.example.demo.ThreadTest - 开始执行子线程...等待1秒钟
 * 20:50:33.434 [Thread-4] INFO com.example.demo.ThreadTest - 开始执行子线程...等待1秒钟
 * 20:50:34.440 [main] INFO com.example.demo.ThreadTest - 恢复执行主线程...
 */

五、线程池技术

package com.example.demo;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.*;

@Slf4j
public class ThreadTest {
    public static void main(String[] args) {
        ExecutorService service = Executors.newFixedThreadPool(10);
        log.info("开始执行主线程...");
        for (int i = 0; i < 10; i++) {
            service.submit(() -> log.info("开始执行子线程..."));
        }
        try {
            service.shutdown();
            if (service.awaitTermination(Integer.MAX_VALUE, TimeUnit.SECONDS)) {
                log.info("恢复执行主线程...");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}
/**
 * 以上代码执行结果
 * 20:55:30.433 [main] INFO com.example.demo.ThreadTest - 开始执行主线程...
 * 20:55:30.605 [pool-1-thread-2] INFO com.example.demo.ThreadTest - 开始执行子线程...
 * 20:55:30.605 [pool-1-thread-1] INFO com.example.demo.ThreadTest - 开始执行子线程...
 * 20:55:30.606 [pool-1-thread-3] INFO com.example.demo.ThreadTest - 开始执行子线程...
 * 20:55:30.608 [pool-1-thread-6] INFO com.example.demo.ThreadTest - 开始执行子线程...
 * 20:55:30.608 [pool-1-thread-5] INFO com.example.demo.ThreadTest - 开始执行子线程...
 * 20:55:30.608 [pool-1-thread-4] INFO com.example.demo.ThreadTest - 开始执行子线程...
 * 20:55:30.609 [pool-1-thread-7] INFO com.example.demo.ThreadTest - 开始执行子线程...
 * 20:55:30.609 [pool-1-thread-8] INFO com.example.demo.ThreadTest - 开始执行子线程...
 * 20:55:30.610 [pool-1-thread-9] INFO com.example.demo.ThreadTest - 开始执行子线程...
 * 20:55:30.611 [pool-1-thread-10] INFO com.example.demo.ThreadTest - 开始执行子线程...
 * 20:55:30.611 [main] INFO com.example.demo.ThreadTest - 恢复执行主线程...
 */
Comments are closed.