java使用多线程时,主线程等待子现场执行完再执行

java使用多线程时,主线程等待子现场执行完再执行

1.问题演示

public static void main(String[] args) {
        // 创建Runnable实例对象
        Runnable runnable = ()->{
            System.out.println("我要一个教练");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("教练来了: " + Thread.currentThread().getName());
            System.out.println("教我游泳,交完后,教练回到了游泳池");
        };

        for(int i=0;i<20;i++){
            Thread thread = new Thread(runnable);
            thread.start();

        }

        System.out.println("************************");

    }

执行效果
在这里插入图片描述
可以看到,子线程还没全部执行完,主线程已经打印出*********了,这是由于多线程里,主线程与创建的20个子线程是异步执行的。但在web项目中,我们需要主线程等待子线程执行完了,再执行主线程,避免拿到的返回结果不对。

2.Thread直接启动的线程执行方式–采用join,阻塞主线程

public static void main(String[] args) {
        // 创建Runnable实例对象
        Runnable runnable = ()->{
            System.out.println("我要一个教练");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("教练来了: " + Thread.currentThread().getName());
            System.out.println("教我游泳,交完后,教练回到了游泳池");
        };
        List<Thread> threads = new ArrayList<>();
        for(int i=0;i<20;i++){
            Thread thread = new Thread(runnable);
            thread.start();
            threads.add(thread);
        }
        //将所有子线程join进主线程
       for (Thread t:threads) {
            try {
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println("************************");


    }

执行效果:
在这里插入图片描述
可以看到*********,在最后才打印了。达到预期效果
注意,千万不要写出这种形式

for(int i=0;i<20;i++){
            Thread thread = new Thread(runnable);
            thread.start();
            thread.join();
        }

因为这个for循环也是主线程管理的,在for循环里面执行thread.join();会导致子线程执行完了,才执行第二个for,起第二个子线程,从执行结果来看,子线程就变成同步执行的了;

3.采用线程池–ExecutorService线程池

3.1采用Future的get()方法
public static void main(String[] args) throws Exception {
        // 创建线程池对象
        ExecutorService service = Executors.newFixedThreadPool(5);//包含2个线程对象
        // 创建Runnable实例对象
        Runnable runnable = ()->{
            System.out.println("我要一个教练");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("教练来了: " + Thread.currentThread().getName());
            System.out.println("教我游泳,交完后,教练回到了游泳池");

        };
        List<Future> futures = new ArrayList<>();
        for(int i=0;i<20;i++){
            Future<?> future = service.submit(runnable);
            futures.add(future);
        }
        for (Future f:futures) {
            f.get();
        }
        System.out.println("************************");
    }

执行结果:
在这里插入图片描述

从执行结果可以发现,主线程的确是等待子线程都执行完了,再执行的主线程,各个子线程之间也是异步执行。
注意:和直接用Thread一样,Future 的get方法也不能用在第一个for里面,以免阻塞了第一个for的正常循环,把子线程搞成了同步执行的效果

for(int i=0;i<5;i++){
            Future<?> future = service.submit(runnable);
            //这种写法是错误的,这个for循环不能被阻塞,不然会导致会导致第一个子线程执行完了,才执行第二个for,起第二个子线程,
            // 从执行结果来看,子线程就变成同步执行的了
            future.get();
        }
3.2 CountDownLatch
public static void main(String[] args) throws Exception {
        final CountDownLatch cdl = new CountDownLatch(5);//参数为线程个数
        // 创建线程池对象
        ExecutorService service = Executors.newFixedThreadPool(5);//包含2个线程对象
        // 创建Runnable实例对象
        Runnable runnable = ()->{
            System.out.println("我要一个教练");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("教练来了: " + Thread.currentThread().getName());
            System.out.println("教我游泳,交完后,教练回到了游泳池");
            cdl.countDown();//此方法是CountDownLatch的线程数-1
        };
        for(int i=0;i<5;i++){
            Future<?> future = service.submit(runnable);
        }

        try {
            cdl.await();//需要捕获异常,当其中线程数为0时这里才会继续运行
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        System.out.println("************************");
    }

执行结果:
在这里插入图片描述

主线程的*******是最后打印的,子线程也是异步执行的,这种方法可行,但有个缺点,就是需要提前确定线程数,不等同与线程池初始化的线程数,这个线程数需要与for循环的执行次数对应,当然大部分情况下,这个数字是可以确认好的。但极少情况下无法确认

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值