java 主线程等待所有子线程执行完毕在执行,在工作总往往会遇到异步去执行某段逻辑, 然后先处理其他事情, 处理完后再把那段逻辑的处理结果进行汇总(比如用户下单一个产品,后台会做一系列的处理,为了提高效率,每个处理都可以用一个线程来执行,所有处理完成了之后才会返回给用户下单成功)的场景, 这时候就需要使用线程了。
解决方法
sleep
用 sleep 方法,让主线程睡眠一段时间,让其它子线程先执行,虽然也是解决的方法,但是不推荐使用。
public class ThreadTest extends Thread {
public static void main(String[] args) throws InterruptedException {
for(int i = 0;i<10;i++) {
Thread th = new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("子线程先执行");
}
});
th.start();
}
Thread.sleep(5000);
System.out.println("主线程执行");
}
}
执行结果如下:
join()
使用 Thread 的 join() 等待所有的子线程执行完毕,主线程在执行,阻塞的是主线程。
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
try {
Thread.sleep(3000);
System.out.println("子线程先执行");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t1.start();
Thread t2 = new Thread(() -> {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t2.start();
//主线程其他工作完毕,等待子线程的结束, 调用join系列的方法即可
t1.join();
t2.join();
System.out.println("主线程执行");
}
isTerminated
isTerminated,当调用shutdown()方法后,并且所有提交的任务完成后才会返回为true
public static void main(String[] args) throws InterruptedException {
ExecutorService pool = Executors.newFixedThreadPool(3);
pool.execute(() -> {
try {
Thread.sleep(2000);
System.out.println("子线程先执行");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
//不再接受新的任务
pool.shutdown();
while (true) {
//不推荐(循环效率很低)
if (pool.isTerminated()) {
System.out.println("线程池中的任务执行结束");
break;
}
}
System.out.println("主线程执行");
}
执行结果如下:
Future机制
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService pool = Executors.newFixedThreadPool(3);
Future<Integer> task = pool.submit(() -> {
try {
Thread.sleep(2000);
System.out.println("子线程先执行");
} catch (InterruptedException e) {
e.printStackTrace();
}
return 2;
});
//不再接受新的任务
pool.shutdown();
//get方法为阻塞获取
System.out.println("task的运行结果:" + task.get());
System.out.println("主线程执行");
}
执行结果如下:
CountDownLatch
每调用一次countDown方法,计数器会减1,在计数器减为0之前,await方法将会阻塞主线程。
public static void main(String[] args) throws InterruptedException, ExecutionException {
CountDownLatch latch = new CountDownLatch(1);
Thread thread = new Thread(() -> {
try {
Thread.sleep(3000);
System.out.println("子线程先执行");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
latch.countDown();
}
});
thread.start();
latch.await();
System.out.println("主线程");
}
执行结果如下:
CompletableFuture
public static void main(String[] args) throws InterruptedException, ExecutionException {
CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(2000);
System.out.println("子线程先执行");
} catch (InterruptedException e) {
e.printStackTrace();
}
return 2;
});
CompletableFuture<Integer> cf = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(2000);
System.out.println("子线程先执行");
} catch (InterruptedException e) {
e.printStackTrace();
}
return 3;
}).thenCombine(cf1, (result1, result2) -> result1 * result2);
//get方法为阻塞获取
System.out.println("计算结果为" + cf.get());
System.out.println("主线程执行");
}
执行结果如下:
CyclicBarrier
public static void main(String[] args) throws InterruptedException, ExecutionException, BrokenBarrierException {
final CyclicBarrier barrier = new CyclicBarrier(5);
for(int i=0;i<5;i++) {
Thread thread = new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("子线程先执行");
try {
barrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
thread.start();
}
barrier.await();
System.out.println("主线程执行");
}
到这里小伙伴可能会问,countDownLatch 和 cyclicBarrier 有什么区别呢?不妨看一下我这篇文章: JUC下的CountDownLatch,CyclicBarrier、Semaphore的使用方法