如何实现ExecutorService中两个线程的Join等待?
如何让ExecutorService中的任务实现顺序执行(第二个任务等待第一个完成)
我来帮你拆解下问题的核心,以及给出正确的实现方案。
你的代码里join()完全不生效的原因很关键:你把自己创建的Thread对象传给ExecutorService.submit()时,这个Thread根本没被真正启动。ExecutorService会把它当作一个普通的Runnable,用线程池里的工作线程去执行它的run()方法,而你创建的Thread-1实例本身的状态始终是NEW——调用一个未启动的Thread的join()会直接返回,自然不会等待任务完成。
正确的实现思路:用Future跟踪任务状态
在ExecutorService的场景下,我们应该用submit()方法返回的Future对象来跟踪任务的完成状态,调用Future.get()方法会阻塞当前线程,直到目标任务执行完成,这才是线程池环境下等待任务结束的标准姿势。
修改后的代码实现
第一步:修改CURD类,传递Future而非Thread
import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import org.springframework.beans.factory.annotation.Autowired; public class CURD { public static ExecutorService executorService = Executors.newCachedThreadPool(); @Autowired private Logging logging; public void Update(List<? extends HBase> save, List<? extends HBase> delete) { // 直接定义任务逻辑,不需要手动包装成Thread Runnable mainTask = () -> { System.out.println("Started Main Task..."); try { Thread.sleep(1500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("End Main Task..."); }; // 提交任务并获取Future对象,用来跟踪任务状态 Future<?> mainTaskFuture = executorService.submit(mainTask); // 将Future传给Logging,代替原来的Thread logging.setPredecessorFuture(mainTaskFuture); } }
第二步:修改Logging类,用Future.get()等待任务完成
import java.util.concurrent.CompletableFuture; import java.util.concurrent.Future; import static com.demo.CURD.executorService; public class Logging { private Future<?> predecessorFuture; // 改为接收Future对象 public void setPredecessorFuture(Future<?> future) { this.predecessorFuture = future; } private void loggingInfo() { Runnable logTask = () ->{ try { if (predecessorFuture != null) { System.out.println(Thread.currentThread().getName() + " Started"); // 调用get()阻塞,直到前置任务完成 predecessorFuture.get(); System.out.println(Thread.currentThread().getName() + " Finished"); } } catch (Exception e) { // 统一捕获中断、任务执行异常 e.printStackTrace(); } }; addTask(logTask); } public void addTask(Runnable run) { System.out.println("Runnable Thread logAround....."); CompletableFuture.runAsync(run, executorService) .exceptionally(ex -> { System.out.println("exception occurred " + ex); return null; }); } }
更简洁的方案:用CompletableFuture链式调用
如果你的需求就是“第二个任务必须在第一个任务完成后执行”,可以直接用CompletableFuture的链式调用,不需要手动传递Future,代码更简洁:
public void Update(List<? extends HBase> save, List<? extends HBase> delete) { Runnable mainTask = () -> { System.out.println("Started Main Task..."); try { Thread.sleep(1500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("End Main Task..."); }; Runnable logTask = () -> { System.out.println(Thread.currentThread().getName() + " Started"); System.out.println(Thread.currentThread().getName() + " Finished"); }; // 链式调用,logTask会自动在mainTask完成后执行 CompletableFuture.runAsync(mainTask, executorService) .thenRunAsync(logTask, executorService) .exceptionally(ex -> { System.out.println("exception occurred " + ex); return null; }); }
关键注意点
- 不要在ExecutorService中传递自己创建的
Thread对象,线程池的核心就是统一管理线程,手动创建Thread会破坏线程池的设计,还会导致状态混乱。 Future.get()会抛出检查异常,需要统一捕获处理(比如中断异常、任务执行时的异常)。- 如果需要更复杂的任务依赖关系,CompletableFuture提供了非常丰富的API(比如
thenApply、thenCombine等),可以满足大部分异步编排需求。
内容的提问来源于stack exchange,提问作者Java Carzy
相关产品推荐
相关产品推荐

