java 线程池如何知道一个线程的任务已经执行完成?

Java线程池通过以下几种机制来知道线程任务是否执行完成:

1. Future对象机制(最常用)

ExecutorService executor = Executors.newFixedThreadPool(5);

// 提交任务,返回Future对象
Future<?> future = executor.submit(() -> {
    // 任务逻辑
    System.out.println("任务执行中...");
    Thread.sleep(1000);
    return "结果";
});

// 1. 阻塞等待结果
try {
    String result = (String) future.get(); // 阻塞直到任务完成
    System.out.println("任务完成,结果:" + result);
} catch (Exception e) {
    e.printStackTrace();
}

// 2. 非阻塞检查状态
while (!future.isDone()) {
    System.out.println("任务还在执行中...");
    Thread.sleep(100);
}
System.out.println("任务已完成:" + future.isDone());

2. CompletionService(批量任务监控)

ExecutorService executor = Executors.newFixedThreadPool(5);
CompletionService<String> completionService = 
    new ExecutorCompletionService<>(executor);

// 提交多个任务
for (int i = 0; i < 5; i++) {
    final int taskId = i;
    completionService.submit(() -> {
        Thread.sleep(taskId * 1000L);
        return "任务" + taskId + "完成";
    });
}

// 按完成顺序获取结果
for (int i = 0; i < 5; i++) {
    Future<String> future = completionService.take(); // 阻塞直到有任务完成
    System.out.println(future.get());
}

3. CompletableFuture(Java 8+,推荐)

ExecutorService executor = Executors.newFixedThreadPool(5);

// 异步执行,添加回调
CompletableFuture.supplyAsync(() -> {
    System.out.println("任务开始执行...");
    Thread.sleep(2000);
    return "执行结果";
}, executor).thenAccept(result -> {
    // 任务完成后的回调
    System.out.println("任务完成,结果:" + result);
}).exceptionally(ex -> {
    // 异常处理
    System.out.println("任务执行异常:" + ex.getMessage());
    return null;
});

// 等待所有任务完成
CompletableFuture<Void> allTasks = CompletableFuture.allOf(
    CompletableFuture.runAsync(() -> System.out.println("任务1"), executor),
    CompletableFuture.runAsync(() -> System.out.println("任务2"), executor)
);
allTasks.get(); // 阻塞直到所有任务完成

4. 线程池本身的监控方法

ThreadPoolExecutor executor = new ThreadPoolExecutor(
    2, 4, 60, TimeUnit.SECONDS, 
    new LinkedBlockingQueue<>(10)
);

executor.execute(() -> {
    System.out.println("任务执行中");
});

// 监控线程池状态
System.out.println("已完成任务数:" + executor.getCompletedTaskCount());
System.out.println("活跃线程数:" + executor.getActiveCount());
System.out.println("队列大小:" + executor.getQueue().size());

5. 同步工具辅助

ExecutorService executor = Executors.newFixedThreadPool(3);
CountDownLatch latch = new CountDownLatch(5);

for (int i = 0; i < 5; i++) {
    executor.execute(() -> {
        try {
            // 执行任务
            Thread.sleep(1000);
        } finally {
            latch.countDown(); // 任务完成,计数器减1
        }
    });
}

latch.await(); // 阻塞直到计数器为0
System.out.println("所有任务已完成");
executor.shutdown();

6. 自定义线程池扩展

class MonitoringThreadPool extends ThreadPoolExecutor {
    
    public MonitoringThreadPool() {
        super(2, 4, 60, TimeUnit.SECONDS, 
              new LinkedBlockingQueue<>());
    }
    
    @Override
    protected void afterExecute(Runnable r, Throwable t) {
        super.afterExecute(r, t);
        // 任务执行完成后自动调用
        System.out.println("任务执行完成");
        if (t != null) {
            System.out.println("任务异常:" + t.getMessage());
        }
    }
}

线程池任务完成的内部机制

  1. 任务队列管理:线程池维护一个任务队列,工作线程从队列中获取任务执行
  2. Worker线程循环:每个Worker线程不断从队列中获取任务执行
  3. Future封装submit()方法将任务封装为FutureTask
  4. 状态标记:任务执行完毕后会修改Future的状态
  5. 结果存储:执行结果存储在Future中,异常被封装

最佳实践建议

  • 使用 CompletableFuture​ 进行异步编程(Java 8+)
  • 批量任务使用 CompletionService
  • 需要精确控制时使用 CountDownLatch/CyclicBarrier
  • 避免在业务代码中轮询检查,尽量使用回调或阻塞等待
  • 注意正确处理异常,避免任务静默失败

作 者:南烛
链 接:https://www.itnotes.top/archives/927
来 源:IT笔记
文章版权归作者所有,转载请注明出处!


上一篇
下一篇