主线程开启子线程执行任务后,主线程hang住,等待子线程执行完成后再继续执行。
梳理具体的实现方法:
1、CountDownLaunch实现
子程序执行完成后,launch--。
private static void caseByCountDownLaunch() {
CountDownLatch countDownLatch = new CountDownLatch(2);
new Thread(() -> {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
countDownLatch.countDown();
System.out.println(System.currentTimeMillis() + " 线程1执行完成。");
}).start();
new Thread(() -> {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
countDownLatch.countDown();
System.out.println(System.currentTimeMillis() + " 线程2执行完成。");
}).start();
System.out.println(System.currentTimeMillis() + " 主线程执行到这里了");
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(System.currentTimeMillis() + " 主线程执行完");
复制
2、Thread.join方法
官方文档解释:主线程的代码块中遇到join()方法时,主线程需要等待子线程结束了(Waits for this thread to die.),才能继续执行join()之后的代码块。
/**
* Thread.join()
*/
private static void caseByThreadJoin() {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(System.currentTimeMillis() + " 线程1执行完成。");
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(System.currentTimeMillis() + " 线程2执行完成。");
}
});
System.out.println(System.currentTimeMillis() + " 主线程执行到这里了");
try {
t1.start();
t2.start();
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(System.currentTimeMillis() + " 主线程执行完");
}
复制
3、ThreadPool#isTerminal
判断线程池中线程是否都执行完了,都执行完后,主线程执行。
private static void caseByThreadPoolIsTerminal() {
ExecutorService pool = Executors.newFixedThreadPool(2);
pool.execute(new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(System.currentTimeMillis() + " 线程1执行完成。");
}
}));
pool.execute(new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(System.currentTimeMillis() + " 线程2执行完成。");
}
}));
pool.shutdown(); //不关闭的情况,线程池会一直开启,等待
System.out.println(System.currentTimeMillis() + " 主线程执行到这里了");
while (!pool.isTerminated()) {
System.out.println("主线程等待");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(System.currentTimeMillis() + " 主线程执行完");
}
复制
4、Future
Future.get()阻塞,主线程实现等待。
private static void caseByFuture() {
ExecutorService pool = Executors.newFixedThreadPool(2);
Future<Integer> f1 = pool.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
Thread.sleep(3000);
System.out.println(System.currentTimeMillis() + " 线程1执行完成");
return 1;
}
});
Future<Integer> f2 = pool.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
Thread.sleep(3000);
System.out.println(System.currentTimeMillis() + " 线程2执行完成");
return 1;
}
});
pool.shutdown(); //不关闭用户线程,jvm一直在等待
System.out.println(System.currentTimeMillis() + " 主线程执行到这里了");
try {
f1.get();
f2.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println(System.currentTimeMillis() + " 主线程执行完");
}
复制
5、CompletableFuture同上
private static void caseByCompletableFuture() {
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(new Supplier<Integer>() {
@Override
public Integer get() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " " + System.currentTimeMillis() + " 线程1执行到这里");
return 1;
}
});
System.out.println(Thread.currentThread().getName() + " " + System.currentTimeMillis() + " 主线程执行到这里了");
future.join();
System.out.println(Thread.currentThread().getName() + " " + System.currentTimeMillis() + " 主线程执行完");
}
复制
6、CyclicBarrier方式
/**
* 通过CyclicBarrier实现
* CyclicBarrier 基于Condition来实现的。在CyclicBarrier类的内部有一个计数器,每个线程在到达屏障点的时候都会调用await方法将自己阻塞,此时计数器会减1,当计数器减为0的时候所有因调用await方法而被阻塞的线程将被唤醒。这就是实现一组线程相互等待的原理。
* CountDownLatch是线程组之间的等待,即一个(或多个)线程等待N个线程完成某件事情之后再执行;而CyclicBarrier则是线程组内的等待,即每个线程相互等待,即N个线程都被拦截之后,然后依次执行。
* CountDownLatch是减计数方式,而CyclicBarrier是加计数方式。
* CountDownLatch计数为0无法重置,而CyclicBarrier计数达到初始值,则可以重置。
* CountDownLatch不可以复用,而CyclicBarrier可以复用。
*/
private static void caseByCyclicBarrier() {
CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " " + System.currentTimeMillis() + " 线程1执行完成。");
}
}).start();
new Thread(new Runnable() {
@Override
public void run(){
try {
Thread.sleep(6000);
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " " + System.currentTimeMillis() + " 线程2执行完成。");
}
}).start();
System.out.println(Thread.currentThread().getName() + " " + System.currentTimeMillis() + " 主线程执行到这里了");
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " " + System.currentTimeMillis() + " 主线程执行完");
}
复制
7、Thread#isAlive实现
private static void caseByThreadIsAlive() {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + System.currentTimeMillis() + " 线程1执行完成。");
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(System.currentTimeMillis() + " 线程2执行完成。");
}
}).start();
System.out.println(Thread.currentThread().getName() + System.currentTimeMillis() + " 主线程执行到这里了");
while (Thread.activeCount() > 2) {
System.out.println("Thread.activeCount()=" + Thread.activeCount());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 主线程等待");
}
System.out.println(Thread.currentThread().getName() + System.currentTimeMillis() + " 主线程执行完");
}
复制
8、并发锁、同步控制等方式
private static void caseByAtomicInteger() {
AtomicInteger atomicInteger = new AtomicInteger(2);
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
atomicInteger.decrementAndGet();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + System.currentTimeMillis() + " 线程1执行完成。");
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
atomicInteger.decrementAndGet();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(System.currentTimeMillis() + " 线程2执行完成。");
}
}).start();
System.out.println(Thread.currentThread().getName() + System.currentTimeMillis() + " 主线程执行到这里了");
while (atomicInteger.get() != 0) {
System.out.println("Thread.activeCount()=" + Thread.activeCount());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 主线程等待");
}
System.out.println(Thread.currentThread().getName() + System.currentTimeMillis() + " 主线程执行完");
}
复制
文章转载自小怂读书,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。
评论
相关阅读
Java萌新修炼手册⑥:面向对象の修仙奥义——从"散修"到"宗门大佬"的基因飞升!
让天下没有难学的编程
45次阅读
2025-04-25 10:10:40
Java萌新修炼手册②:Hello Worldの108种写法——从入门到入坟!
让天下没有难学的编程
41次阅读
2025-04-21 10:34:36
从 Java 到 Go:面向对象的巨人与云原生的轻骑兵
京东云开发者
36次阅读
2025-04-25 11:41:37
Java萌新修炼手册⑤:数组の千层套路——从"鸽子笼"到"摩天楼"的进阶之路!
让天下没有难学的编程
32次阅读
2025-04-25 10:10:41
Java数据库连接池学习
淡定
32次阅读
2025-04-14 22:46:26
Java萌新修炼手册④:流程控制の三十六计——让代码学会"见风使舵"!
让天下没有难学的编程
27次阅读
2025-04-23 14:33:55
Java萌新修炼手册①:开局一把JDK,环境搭建全靠浪!
让天下没有难学的编程
21次阅读
2025-04-21 10:34:37
java浅拷贝BeanUtils.copyProperties引发的RPC异常
京东云开发者
19次阅读
2025-04-30 17:10:50
Java程序使用预处理语句的性能提升
GreatSQL社区
17次阅读
2025-04-23 11:18:50
面试官:Java反射和new效率对比,差距有多大?
捡田螺的小男孩
17次阅读
2025-04-13 10:34:43