并发--Callable vs Runnable
核心对比总表(Callable vs Runnable)
维度 Runnable Callable 接口定义 public interface Runnable
public interface Callable<V>
核心方法 void run()
V call() throws Exception
返回值 ❌ 无返回值 ✅ 可返回泛型结果 异常处理 ❌ 只能处理非受检异常 ✅ 可抛出任意受检/非受检异常 执行方式 Thread/Executor.execute() ExecutorService.submit()[也可以通过Thread执行,一般不用] 适用场景 简单异步任务 需要结果/异常处理的复杂任务 1. 接口定义与核心方法
Runnable(Java 1.0引入)
@FunctionalInterface public interface Runnable {// 无参数、无返回值、无异常声明void run(); }
使用示例:
Runnable printTask = () -> {System.out.println("执行Runnable任务");// 不能返回结果,不能抛出受检异常 };// 执行方式 new Thread(printTask).start(); // 或 executor.execute(printTask);
Callable(Java 5引入)
public interface Callable<V> {// 返回泛型结果,可抛出任何异常V call() throws Exception; }
使用示例:
Callable<Integer> calcTask = () -> {System.out.println("执行Callable任务");if (Math.random() > 0.8) {throw new IOException("模拟IO异常");}return 42; // 返回计算结果 };// 执行方式 ExecutorService executor = Executors.newSingleThreadExecutor(); Future<Integer> future = executor.submit(calcTask);//使用Thread也可以,但是Callable不推荐用 FutureTask<Integer> future = new FutureTask(calcTask); Thread t = new Thread(future); Integer integer = future.get(); //可以获取到calcTask 返回的结果【42】
2. 异常处理机制对比
Runnable的异常困境
Runnable fileTask = () -> {try {Files.copy(source, target); // 可能抛出IOException} catch (IOException e) {// 只能转为非受检异常throw new RuntimeException(e);} };// 未捕获异常会导致线程终止 Thread t = new Thread(fileTask); //使用 Thread.setUncaughtExceptionHandler为线程设置异常处理器,当线程因未捕获的异常终止时会触发该处理器。 //不用Thread.setUncaughtExceptionHandler 线程x就会报错 t.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {@Overridepublic void uncaughtException(Thread t, Throwable e) {System.out.println("捕获到异常: " + e.getMessage());// 可以在这里记录日志、通知其他组件等} }); t.start();
Callable的完整异常链
Callable<String> dbTask = () -> {if (dbConnection.isClosed()) {throw new SQLException("数据库未连接");}return fetchData(); };Future<String> future = executor.submit(dbTask);try {String data = future.get();process(data); } catch (ExecutionException e) {Throwable cause = e.getCause(); // 获取原始异常if (cause instanceof SQLException) {reconnectDatabase();} }