1)使用 Thread.join():
主线程通过调用 join() 方法等待子线程执行完毕。子线程正常结束,说明执行成功,若抛出异常则需要捕获处理。
2)使用 Callable 和 Future:
通过 Callable 创建可返回结果的任务,并通过 Future.get() 获取子线程的执行结果或捕获异常。Future.get() 会阻塞直到任务完成,若任务正常完成,返回结果,否则抛出异常。
3)使用回调机制:
可以通过自定义回调机制,主线程传入一个回调函数,子线程完成后调用该函数并传递执行结果。这样可以非阻塞地通知主线程任务完成情况。
4)使用 CountDownLatch或其他 JUC 相关类:
主线程通过 CountDownLatch 来等待子线程完成。当子线程执行完毕后调用 countDown(),主线程通过 await() 等待子线程完成任务。
Thread.join() 代码示例
public class ThreadJoinExample {public static void main(String[] args) {Thread t = new Thread(() -> {try {// 模拟任务Thread.sleep(1000);System.out.println("子线程执行完成");} catch (InterruptedException e) {e.printStackTrace();}});t.start();try {// 主线程等待子线程完成t.join();System.out.println("主线程确认子线程执行完毕");} catch (InterruptedException e) {e.printStackTrace();}}
}
Callable 和 Future 代码示例
import java.util.concurrent.*;public class CallableFutureExample {public static void main(String[] args) {ExecutorService executor = Executors.newSingleThreadExecutor();Callable<Boolean> task = () -> {try {// 模拟任务Thread.sleep(1000);System.out.println("子线程执行完成");return true; // 返回任务是否成功} catch (InterruptedException e) {e.printStackTrace();return false;}};Future<Boolean> future = executor.submit(task);try {// 获取子线程的执行结果boolean result = future.get();System.out.println("子线程执行成功:" + result);} catch (InterruptedException | ExecutionException e) {e.printStackTrace();} finally {executor.shutdown();}}
}
引申问题 Callable 是否只能配合 线程池使用?
callable 可以不配合线程池(不推荐)
thread 只能直接运行 Runable,Callable 需要通过 FutureTask(或线程池)包装成 Runnable 类型才能被线程执行。
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;public class CallableWithoutThreadPool {public static void main(String[] args) {// 1. 定义Callable任务Callable<Boolean> task = () -> {try {Thread.sleep(1000);System.out.println("子线程执行完成");return true;} catch (InterruptedException e) {e.printStackTrace();return false;}};// 2. 用FutureTask包装Callable(关键:FutureTask实现了Runnable)FutureTask<Boolean> futureTask = new FutureTask<>(task);// 3. 直接创建Thread执行FutureTask(无需线程池)Thread thread = new Thread(futureTask);thread.start();try {// 4. 获取Callable的执行结果boolean result = futureTask.get();System.out.println("子线程执行成功:" + result);} catch (InterruptedException | java.util.concurrent.ExecutionException e) {e.printStackTrace();}}
}
Thread 只能直接运行 Runnable , 线程池可以运行什么?
- 基础类型:Runnable(和 Thread 一致)
这是最基础的任务类型,线程池支持两种执行方式:
execute(Runnable):无返回值,仅执行任务,是最轻量化的方式;
submit(Runnable):有返回值(Future<?>),可通过 Future 判断任务是否完成(但无法获取业务结果)。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;public class ThreadPoolRunnable {public static void main(String[] args) throws Exception {ExecutorService executor = Executors.newSingleThreadExecutor();// 方式1:execute执行Runnable(无返回)executor.execute(() -> System.out.println("execute执行Runnable任务"));// 方式2:submit执行Runnable(返回Future,仅判断完成状态)Future<?> future = executor.submit(() -> System.out.println("submit执行Runnable任务"));System.out.println("任务是否完成:" + future.isDone()); // 输出true/falseexecutor.shutdown();}
}
- 核心优势类型:Callable
(线程池专属核心能力)
这是线程池相比 Thread 最核心的优势 —— 支持有返回值、能抛出受检异常的 Callable 任务,只能通过submit(Callable)执行,返回Future ,可通过future.get()获取业务结果。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;public class ThreadPoolCallable {public static void main(String[] args) throws Exception {ExecutorService executor = Executors.newSingleThreadExecutor();// submit执行Callable,返回Future<Long>(有业务返回值)Future<Long> future = executor.submit(() -> {Thread.sleep(500);return System.currentTimeMillis(); // 返回当前时间戳});// 获取Callable的执行结果Long result = future.get();System.out.println("Callable返回结果:" + result);executor.shutdown();}
}
- 批量任务:Collection<? extends Callable
>
线程池还支持批量提交 Callable 任务,通过invokeAll()和invokeAny()实现:
invokeAll(Collection<Callable>):等待所有任务完成,返回List<Future >,按提交顺序对应每个任务的结果;
invokeAny(Collection<Callable>):返回第一个完成且无异常的任务结果,同时取消其他未完成的任务。
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;public class ThreadPoolBatchCallable {public static void main(String[] args) throws Exception {ExecutorService executor = Executors.newFixedThreadPool(3);// 构建批量Callable任务List<Callable<Integer>> tasks = new ArrayList<>();tasks.add(() -> { Thread.sleep(300); return 1; });tasks.add(() -> { Thread.sleep(100); return 2; });tasks.add(() -> { Thread.sleep(200); return 3; });// 方式1:invokeAll - 等待所有任务完成List<Future<Integer>> allResults = executor.invokeAll(tasks);for (Future<Integer> f : allResults) {System.out.println("invokeAll结果:" + f.get());}// 方式2:invokeAny - 获取第一个完成的任务结果(此处会返回2)Integer firstResult = executor.invokeAny(tasks);System.out.println("invokeAny结果:" + firstResult);executor.shutdown();}
}
- 扩展类型:Runnable + 自定义返回值
线程池还支持给无返回值的 Runnable 绑定一个自定义返回值,通过submit(Runnable task, V result)执行,返回Future—— 本质是让 Runnable 也能 “模拟” Callable 的返回能力。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;public class ThreadPoolRunnableWithResult {public static void main(String[] args) throws Exception {ExecutorService executor = Executors.newSingleThreadExecutor();// 自定义返回值对象String customResult = "任务执行成功";// submit(Runnable, 自定义返回值)Future<String> future = executor.submit(() -> {System.out.println("执行Runnable任务");}, customResult);// 获取绑定的返回值String result = future.get();System.out.println("Runnable绑定的返回值:" + result); // 输出“任务执行成功”executor.shutdown();}
}
| 方法 | 支持的任务类型 | 返回值 | 异常处理 |
|---|---|---|---|
| execute() | 仅 Runnable | void(无返回) | 异常直接抛出,无法通过返回值捕获 |
| submit() | Runnable/Callable | Future<?>/Future |
异常被封装到 Future 中,调用get()时抛出 ExecutionException |
总结
线程池核心支持的任务类型:Runnable(基础)、Callable
execute()仅支持 Runnable 且无返回,submit()支持所有任务类型且返回 Future(可获取结果 / 判断状态 / 捕获异常);
线程池对 Callable 的支持是内部通过FutureTask包装实现的(和你之前问的 “手动执行 Callable” 逻辑一致),无需开发者手动处理。
-------------------------------------------引申结束-------------------------------------------
回调机制
public class CallbackExample {interface Callback {void onComplete(boolean success);}static class WorkerThread extends Thread {private final Callback callback;WorkerThread(Callback callback) {this.callback = callback;}@Overridepublic void run() {try {// 模拟任务Thread.sleep(1000);System.out.println("子线程执行完成");callback.onComplete(true); // 通知主线程任务完成} catch (InterruptedException e) {e.printStackTrace();callback.onComplete(false);}}}public static void main(String[] args) {Callback callback = success -> System.out.println("子线程执行成功: " + success);WorkerThread worker = new WorkerThread(callback);worker.start();// 可死循环监听 success 状态}
}
提前实例化一个对象,传递过去, 让对象去调用实现类的方法
CountDownLatch 示例代码
import java.util.concurrent.CountDownLatch;public class CountDownLatchExample {public static void main(String[] args) throws InterruptedException {CountDownLatch latch = new CountDownLatch(1);Thread t = new Thread(() -> {try {// 模拟任务Thread.sleep(1000);System.out.println("子线程执行完成");} catch (InterruptedException e) {e.printStackTrace();} finally {latch.countDown(); // 通知主线程}});t.start();// 等待子线程完成latch.await();System.out.println("主线程确认子线程执行完毕");}
}
多线程计数法,latch.countDown()为0时,latch.await();才放行
ExecutorCompletionService
可以使用 ExecutorCompletionService 来同时提交多个任务,并获取其执行结果。take() 方法可以从结果队列中获取已完成的任务。
import java.util.concurrent.*;public class CompletionServiceExample {public static void main(String[] args) {ExecutorService executor = Executors.newFixedThreadPool(2);CompletionService<Boolean> completionService = new ExecutorCompletionService<>(executor);completionService.submit(() -> {Thread.sleep(1000);System.out.println("子线程1执行完成");return true;});completionService.submit(() -> {Thread.sleep(500);System.out.println("子线程2执行完成");return true;});try {for (int i = 0; i < 2; i++) {Future<Boolean> result = completionService.take();System.out.println("任务执行成功:" + result.get());}} catch (InterruptedException | ExecutionException e) {e.printStackTrace();} finally {executor.shutdown();}}
}
ExecutorCompletionService 简单说就是内部使用LinkedBlockingQueue存储完成的Future,
Future.get()是按照顺序获取,而ExecutorCompletionService.take(),是按照执行完成顺序获取。
另一个 poll() 不阻塞,但是无结果会返回null.