原创

Java主线程等待子线程执行完毕,线程池运行完成

1.直接上代码了,第一种是利用JDK提供的CountDownLatch类

    @Test
    public void ThreadPoolExecutorT(){
        ThreadPoolExecutor e = new ThreadPoolExecutor(5, 10, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue());
        final CountDownLatch countDownLatch = new CountDownLatch(10);
        for(int i=0;i<10;i++) {
            final int k = i;
            e.execute(new Runnable() {
                public void run() {
                    try {
                        Thread.sleep(2000);
                        Thread.currentThread().setName("ThreadName->k" + k);
                        System.out.println("线程" + Thread.currentThread().getName() + "执行了!");
                    } catch (InterruptedException e) {

                    }finally {
                        countDownLatch.countDown();
                    }
                }
            });
        }
        boolean timeoutFlag = false;
        try {
            timeoutFlag = countDownLatch.await(4, TimeUnit.SECONDS);
        } catch (InterruptedException e1) {
            e1.printStackTrace();
            System.out.println(e1.getMessage());
        }
        if(timeoutFlag)
        {
            System.out.println("所有子线程执行完成");
        }
        System.out.println("执行完了。。。");
    }

1)主线程调用countDownLatch.await()方法后会被阻塞
2)子线程调用countDownLatch.countDown();后countDownLatch内部计数器-1
3)所有子线程执行完毕调用countDown()计数器变为0,这时候主线程.await()方法才会返回

其中重点在于子线程调用countDownLatch.countDown();这句代码一定要写在finally块里面!要防止代码异常导致没有执行countDown();而导致countDownLatch.await超时异常!

2.利用awaitTermination方法,我基本没用过这种方法

public void newSingleThreadExecutorT(){
        ExecutorService s1 = Executors.newSingleThreadExecutor();
        for(int i=0;i<3;i++){
            final int k = i;
            s1.execute(new Runnable() {
                public void run() {
                    Thread.currentThread().setName("ThreadName,k->" + k);
                    try {
                        Thread.sleep(1000);
                        System.out.println(Thread.currentThread().getName() + " k->" + k);
                    } catch (InterruptedException e) {

                    }
                }
            });
        }
        try {
            //等待线程池所有执行完
            if(s1.awaitTermination(10, TimeUnit.SECONDS)){
                s1.shutdownNow();
            }
        }catch (InterruptedException e){
            System.out.printf("00");
        }finally {

        }
        System.out.println("执行完了。。。");

    }

第一个参数指定的是时间,第二个参数指定的是时间单位(当前是秒)。返回值类型为boolean型
如果等待的时间超过指定的时间,但是线程池中的线程运行完毕,那么awaitTermination()返回true。执行分线程已结束。
如果等待的时间超过指定的时间,但是线程池中的线程未运行完毕,那么awaitTermination()返回false。不执行分线程已结束
如果等待时间没有超过指定时间,等待!

仔细阅读参数和对应等待时间和超过指定时间如何处理,一般不会用但是面试会问到哦

3.这个就是用的join方法本质调用线程的wait方法达到同步

public class JoinTh {

    public  void tc(String [] args) throws InterruptedException {
        ThreadT t1 = new ThreadT("aa");
        ThreadT t2 = new ThreadT("bb");
        t1.start();
        t1.join();
        t2.start();
    }

}
class ThreadT extends Thread{
    public ThreadT(String name){
        super(name);
    }
    @Override
    public void run(){
        for(int i=0;i<10;i++){
            System.out.println(this.getName() + "->" + i);
        }
    }
}

1)上面代码就是使用join实现线程同步

简单看一下jdk6中join源码。。(忘了切换jdk版本了就这样吧以后换成jdk8)

    public final void join() throws InterruptedException {
        this.join(0L);
    }
    ......
        public final synchronized void join(long var1) throws InterruptedException {
        long var3 = System.currentTimeMillis();
        long var5 = 0L;
        if (var1 < 0L) {
            throw new IllegalArgumentException("timeout value is negative");
        } else {
            if (var1 == 0L) {
                while(this.isAlive()) {
                    this.wait(0L);
                }
            } else {
                while(this.isAlive()) {
                    long var7 = var1 - var5;
                    if (var7 <= 0L) {
                        break;
                    }

                    this.wait(var7);
                    var5 = System.currentTimeMillis() - var3;
                }
            }

        }
    }

源码分析

1)从源码可以看出来join的顺序不能随便放
2)主线程调用t1.join的时候,会先获取t1的锁(synchronized),然后主线程开始监听t1是否还在执行(this.isAlive()),没有结束之前调用t1的wait()方法(this.wait(0L);)主线程进入等待状态
3)此时t1还在执行,t2.start()还没开始执行,等t1执行完之后,主线会才继续执行t2线程
4)这就是join的保证线程同步和其他线程执行完在执行主线程的原理了

t1.join();使用之后t2会在t1执行完之后在执行,并且join需要在t1 start之后执行才有效果,join也不能放在t2.start之后执行,顺序比较严格所以很不灵活

总结

线程池大多数情况使用countDownLatch,灵活优雅方面,如果用join可想有多痛苦了
在非线程池,单线程需要等待所有子线程执行完可以考虑使用join方法

正文到此结束
本文目录