这是我参与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,祝你升职、加薪、不提桶!
近期评论