美文网首页
主线程等待

主线程等待

作者: 一直在路上_求名 | 来源:发表于2020-05-30 22:09 被阅读0次

    场景介绍

    在实际的工作过程中,为了减少用户的等待时间,通常会使用多线程去并行处理相关任务。主任务线程等待其他并行任务处理完成后,获取执行的结果,经过相关处理,返回给用户。这种方式是多线程在程序中主要的使用方式,因此这就要求主线程必须等待任务线程的执行,然后汇总结果。

    实现方法

    join方法

    在 CountDownLatch 类未出现之前要实现主线程等待只能使用 join 方法(这样说有点绝对,因为 Future 的 get 方法也能实现闭锁的功能,但是需要自己将其缓存后再去循环处理,不是使用 JDK 所提供的方法了)。join 方法和之前介绍的方法不同,它是线程 Thread 类的方法,它没有入参也没有返回值。

    示例代码
    public static void main(String[] args) throws InterruptedException {
            System.out.println("main start");
            Thread t1 = new Thread(() -> {
                System.out.println("t1 start");
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("t1 end");
            });
            Thread t2 = new Thread(() -> {
                System.out.println("t2 start");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("t2 end");
            });
            t1.start();
            t2.start();
            t1.join();
            t2.join();
            System.out.println("main end");
        }              
    

    如上代码中,创建了两个子线程 t1 和 t2, 然后分别启动两个子线程并调用各自的 join 方法。方法执行后,会先执行两个子线程,执行完后才会执行主线程的打印任务。
    执行过程为,当主线程执行到 t1.join() 的时候其会被阻塞,等待 t1 执行完后再执行 t2.join() 会再次被阻塞,等到 t2 执行完后,再执行主线程的打印任务。
    需要注意的是,要先启动后再调用 join 方法才会有用,如果先调用 join 方法再启动是不会生效的。

    CountDownLatch

    上面我们介绍了通过使用线程类的 join 方法来让主线程等待,但是这个方法不够灵活,对实际工作的各种场景并不能完全满足,因此 JDK 提供了 CountDownLatch 类,可以让我们能更好的实现该功能。

    示例代码
    public static void main(String[] args) throws InterruptedException {
            System.out.println("main start");
            CountDownLatch countDownLatch = new CountDownLatch(2);
            new Thread(() -> {
                try {
                    System.out.println("t1 start");
                    Thread.sleep(500);
                    System.out.println("t1 end");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    countDownLatch.countDown();
                }
            }).start();
            new Thread(() -> {
                try {
                    System.out.println("t2 start");
                    Thread.sleep(1000);
                    System.out.println("t2 end");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    countDownLatch.countDown();
                }
            }).start();
            countDownLatch.await();
            System.out.println("main end");
        }
    }
    

    如上代码中,创建了一个 CountDownLatch 对象,由于有两个子线程,所以构造函数传入的参数为2。然后创建了两个子线程,在各自的 finally 模块中调用 CountDownLatch 对象的 countDown 方法;分别启动两个子线程,最后由主线程调用 CountDownLatch 对象的 await 方法,并执行打印任务。
    代码的执行过程为,在创建 CountDownLatch 对象时,构造方法传入了计数器数量2,主线程调用 CountDownLatch 对象的 await 方法时将会被阻塞,直到 CountDownLatch 对象中的计数器变为0,主线程才会返回,阻塞也就结束了。在子线程执行时,由于调用了 CountDownLatch 对象的 countDown 方法,每次调用计数器都会减1 ,两次调用后计数器变为0,主线程结束阻塞打印日志。
    需要注意的是,CountDownLatch 对象的 await 方法被返回时,只需要 CountDownLatch 对象的计数器变为0即可,所以只需要调用 countDown 方法的次数为计数器的数量即可,并不需要各线程都执行完成,示例代码如下:

    public static void main(String[] args) throws InterruptedException {
            System.out.println("main start");
            CountDownLatch countDownLatch = new CountDownLatch(2);
            new Thread(() -> {
                try {
                    System.out.println("t1 start");
                    Thread.sleep(2000);
                    System.out.println("t1 end");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    countDownLatch.countDown();
                }
            }).start();
            new Thread(() -> {
                try {
                    System.out.println("t2 start");
                    Thread.sleep(1000);
                    System.out.println("t2 end");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    countDownLatch.countDown();
                    countDownLatch.countDown();
                }
            }).start();
            countDownLatch.await();
            System.out.println("main end");
        }
    

    总结

    1、join 方法是 Thread 类的方法,调用它时主线程会阻塞到调用它的线程执行完后才执行。
    2、CountDownLatch 类是 JDK 专门提供的一个用来操作线程等待的类,它是使用计数器的方法,并不关心各线程是否执行完成,因此它更加的灵活。
    3、由于在实际工作中,常常使用线程池来管理程序中的线程,因此使用 CountDownLatch 类来处理会更加灵活实用。

    相关文章

      网友评论

          本文标题:主线程等待

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