如何判断线程结束了

这是我参与8月更文挑战的第11天,活动详情查看:8月更文挑战

WangScaler: 一个用心创作的作者。

声明:才疏学浅,如有错误,恳请指正。

多线程、分布式、高并发是常见的面试题,所以也是我们必须掌握的知识与要点。掌握的起端就是学习,知识就是这样一点点的积累来的,那么今天我们一起来学习学习与多程序有关的知识,如何判断线程结束。

示例

在main函数启动6个新的线程,main函数所在的线程会等待这6个线程结束,再往下执行?还是main线程启动完6个线程之后直接往下执行呢?示例如下:

package com.wangscaler.lock;
​
/**
 * @author WangScaler
 * @date 2021/8/9 15:47
 */public class ThreadEnd {
    public static void main(String[] args) {
        int num = 6;
        for (int i = 0; i < num; i++) {
            new Thread(() -> {
                String name = "线程" + Thread.currentThread().getName();
                System.out.println(name + "正在执行");
            }, String.valueOf(i)).start();
        }
        System.out.println("所有线程执行完毕");
    }
}
复制代码

在单线程中,程序自上而下执行,所以你可能以为上面6个线程都执行完之后,才会打印所有线程执行完毕,然而事实并非如此,这里是多线程,6个线程的执行与main线程没有直接关联,所以执行结果如下:

线程0正在执行
线程2正在执行
线程1正在执行
线程3正在执行
所有线程执行完毕
线程4正在执行
线程5正在执行
复制代码

多线程是同时执行的,mian线程启动完新的线程之后,将继续向下执行,而新的6个线程的执行过程与main线程是没有关联的,那么我们如何确保6个线程都执行完毕呢?来一起看看下面几种解决方案。

1、CountDownLatch

正如他的名字一样,是个递减的计数器,每调用一次countDown(),计数器减一。起初我们生成CountDownLatch对象时,指定他的数量和线程数一致。每当线程执行结束的时候,调用countDown()进行减一。

示例如下:

package com.wangscaler.lock;
​
import java.util.concurrent.CountDownLatch;
​
/**
 * @author WangScaler
 * @date 2021/8/9 15:47
 */public class ThreadEnd {
    public static void main(String[] args) throws InterruptedException {
        int num = 6;
        CountDownLatch countDownLatch = new CountDownLatch(num);
        for (int i = 0; i < num; i++) {
            new Thread(() -> {
                String name = "线程" + Thread.currentThread().getName();
                System.out.println(name + "正在执行");
                countDownLatch.countDown();
            }, String.valueOf(i)).start();
        }
        countDownLatch.await();
        System.out.println("6个线程执行完毕");
    }
}
复制代码

使用countDownLatch.await();线程会阻塞,只有线程数减到0之后才会放行。所以当上述6个线程没执行完时,main线程处于阻塞状态,只有当countDownLatch.await();放行的时候,也就证明了上述的6个线程已经执行完毕,将countDownLatch的总数减到了0。

2、CyclicBarrier

与CountDownLatch正好相反,它允许一组线程进行等待(即每个线程完成之后进入等待),直到达到某个公共屏障点(所有线程等待完成),才可以开始。例如我们和朋友去吃饭,有这样的礼仪,人不齐大家就坐着等着,只有人到齐后才可以动筷子。人数就像是一个屏障,阻挡我们吃饭。所以上述的例子也可以这样做。

package com.wangscaler.lock;
​
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
​
/**
 * @author WangScaler
 * @date 2021/8/9 15:47
 */public class ThreadEnd {
    public static void main(String[] args) {
        int num = 6;
        CyclicBarrier cyclicBarrier = new CyclicBarrier(num,()->{
            System.out.println("6个线程执行完毕");
        });
        for (int i = 0; i < num; i++) {
            new Thread(() -> {
                String name = "线程" + Thread.currentThread().getName();
                System.out.println(name + "正在执行");
                try {
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }, String.valueOf(i)).start();
        }
    }
}
复制代码

只有6个线程均进入等待之后,才会执行Runnable,所以我们可以在Runnable判断线程是否结束。即6个线程执行结束,调用Runnable打印6个线程执行完毕

3、activeCount

这个参数 ,我在前边的文章里写过。就是用于返回当前线程的线程中活动线程的数量组。

package com.wangscaler.lock;
​
​
/**
 * @author WangScaler
 * @date 2021/8/9 15:47
 */public class ThreadEnd {
    public static void main(String[] args) {
        int num = 6;
        for (int i = 0; i < num; i++) {
            new Thread(() -> {
                String name = "线程" + Thread.currentThread().getName();
                System.out.println(name + "正在执行");
            }, String.valueOf(i)).start();
        }
        while (Thread.activeCount()>2) {
            Thread.yield();
        }
        System.out.println("6个线程执行完毕");
    }
}
复制代码

当活跃的线程数大于2的时候,循环检测,直到线程数小于等于2时,结束循环继续往下执行。

为什么是activeCount的值大于2呢?因为idea启动会默认的启动一个Monitor Ctrl-Break线程,另一个线程自然就是执行我们的main函数的线程。所以当线程少于2个线程的时候,肯定其他6个线程已经执行完毕。

4、exe.isTerminated()

package com.wangscaler.lock;
​
​
import java.util.concurrent.*;
​
/**
 * @author WangScaler
 * @date 2021/8/9 15:47
 */public class ThreadEnd {
    public static void main(String[] args) throws InterruptedException {
        int num = 6;
        ExecutorService exe = Executors.newFixedThreadPool(num);
        for (int i = 0; i < num; i++) {
           exe.execute(() -> {
               String name = "线程" + Thread.currentThread().getName();
               System.out.println(name + "正在执行");
           });
        }
        exe.shutdown();
        while (true) {
            if (exe.isTerminated()) {
                System.out.println("6个线程执行完毕");
                break;
            }
            Thread.sleep(200);
        }
​
​
    }
}
复制代码

通过线程池来判断,在线程池启动6个线程,当线程执行完毕之后,通过exe.shutdown();关闭线程池,通过线程池是否关闭,从而可以判断线程是否已经执行完毕。

你还知道哪些判断线程结束的方法?分享出来,一块学习,一块进步。

来都来了,点个赞再走呗!

关注WangScaler,祝你升职、加薪、不提桶!