2025年12月22日--在 Java 中主线程如何知晓创建的子线程是否执行成功?

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 , 线程池可以运行什么?

  1. 基础类型: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/false

        executor.shutdown();
    }
}
  1. 核心优势类型: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();
    }
}
  1. 批量任务: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();
    }
}
  1. 扩展类型: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(核心优势),还支持批量 Callable 任务、Runnable + 自定义返回值;
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;
        }

        @Override
        public 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.

posted @ 2025-12-22 18:20  kisshappyboy  阅读(5)  评论(0)    收藏  举报