Skip to content

线程基础与执行器 ​

学习目标 ​

  • 区分 Thread、Runnable、Callable、Future 与线程生命周期。
  • 会用 ExecutorService 管理任务,并设置等待、取消、异常和关闭策略。
  • 知道平台线程、线程池和虚拟线程的适用边界。

核心知识点 ​

专业术语 ​

  • Thread:JVM 中执行代码的线程对象,承载任务的运行上下文。
  • Runnable:没有返回值、不能直接声明受检异常的任务接口。
  • Callable:可以返回结果、抛出异常的任务接口。
  • Executor/ExecutorService:把“提交任务”和“创建、调度线程”分离的执行器抽象。
  • Future:异步任务的句柄,可以等待结果、取消任务或查询完成状态。

白话解释与边界 ​

线程是执行资源,Runnable/Callable 是要执行的工作,ExecutorService 是管理执行资源的容器。小程序可以直接 new Thread;业务代码通常提交给有界线程池或虚拟线程执行器,避免每个请求都自行创建平台线程。线程池并不会让任务天然安全,任务共享数据时仍需要同步。

线程状态包括 NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING 和 TERMINATED。sleep 只让当前线程暂时不运行,不释放已经持有的锁;wait 会释放对应监视器并等待通知。中断是协作式取消信号,不是强制杀线程。

常用用法 ​

启动平台线程:把任务交给 Thread ​

start() 才会创建并调度新线程,直接调用 run() 只是在当前线程执行方法。

java
// 说明:启动平台线程:把任务交给 Thread。
public class ThreadRunnableDemo {
    public static void main(String[] args) throws InterruptedException {
        Runnable task = () -> {
// 初始状态:task = () ->。
            System.out.println("worker=" + Thread.currentThread().getName());
// 输出:worker=worker-1
        };
        Thread thread = new Thread(task, "worker-1");
// 初始状态:thread = new Thread(task, "worker-1")。
// 作用:Thread thread = new Thread(task, "worker-1");;把任务交给 Thread。
        thread.start();
// 初始状态:thread.start()。
// 作用:thread.start();;把任务交给 Thread。
        thread.join();
// 初始状态:thread.join()。
// 作用:thread.join();;把任务交给 Thread。
        System.out.println("finished=" + thread.getState());
// 输出:finished=TERMINATED
    }
}

需要等待线程结束时调用 join(),否则主线程可能先结束。

Thread.join:等待线程完成 ​

join() 建立了“线程结束先于后续读取”的 happens-before 关系。

java
// 说明:Thread.join:等待线程完成。
public class ThreadJoinDemo {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
// 作用:Thread thread = new Thread(() -> {;等待线程完成。
// 初始状态:thread = new Thread(() ->。
            // 这里模拟一小段确定的工作
        });
        thread.start();
// 作用:thread.start();;等待线程完成。
        thread.join();
// 初始状态:thread.join()。
// 作用:thread.join();;等待线程完成。
        System.out.println("worker completed");
// 输出:worker completed
    }
}

join(timeout) 只等待一段时间,返回时线程可能仍在运行,不能把超时当成完成。

提交有结果任务:用 Future 观察完成状态 ​

submit 返回 Future;Runnable 的结果通常是 null,Callable 可以返回值。

java
// 说明:提交有结果任务:用 Future 观察完成状态。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class ExecutorSubmitDemo {
    public static void main(String[] args) throws Exception {
        ExecutorService executor = Executors.newFixedThreadPool(2);
// 初始状态:executor = Executors.newFixedThreadPool(2)。
// 作用:ExecutorService executor = Executors.newFixedThreadPool(2);;用 Future 观察完成状态。
        try {
            Future<?> done = executor.submit(() -> {
// 初始状态:done = executor.submit(() ->。
// 作用:Future<?> done = executor.submit(() -> {;用 Future 观察完成状态。
                System.out.println("task=ok");
// 输出:task=ok
            });
            done.get();
// 初始状态:done.get()。
// 作用:done.get();;用 Future 观察完成状态。
            Future<Integer> result = executor.submit(() -> 20 + 22);
// 初始状态:result = executor.submit(() -> 20 + 22)。
// 作用:Future<Integer> result = executor.submit(() -> 20 + 22);;用 Future 观察完成状态。
            System.out.println("answer=" + result.get());
// 输出:answer=42
        } finally {
            executor.shutdown();
// 初始状态:executor.shutdown()。
// 作用:executor.shutdown();;用 Future 观察完成状态。
        }
    }
}

get() 会等待并把任务异常包装成 ExecutionException,调用方应决定记录、重试还是向上抛出。

ExecutorService.execute:只提交无结果任务 ​

execute 没有 Future,也没有统一的返回结果;未捕获异常会交给线程的 UncaughtExceptionHandler。

java
// 说明:ExecutorService.execute:只提交无结果任务。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ExecutorExecuteDemo {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService executor = Executors.newSingleThreadExecutor();
// 作用:ExecutorService executor = Executors.newSingleThreadExecutor();;只提交无结果任务。
// 初始状态:executor = Executors.newSingleThreadExecutor()。
        try {
            executor.execute(() -> {
// 作用:executor.execute(() -> {;只提交无结果任务。
                System.out.println("execute once");
// 输出:execute once
            });
        } finally {
            executor.shutdown();
// 作用:executor.shutdown();;只提交无结果任务。
            executor.awaitTermination(1, java.util.concurrent.TimeUnit.SECONDS);
// 作用:executor.awaitTermination(1, java.util.concurrent.TimeUnit.SECONDS);;只提交无结果任务。
        }
    }
}

需要观察任务成功、失败或取消时优先使用 submit 并检查 Future。

ScheduledExecutorService:延迟或周期调度 ​

scheduleAtFixedRate 按计划时间尝试执行,scheduleWithFixedDelay 按上次执行结束后再等待;周期任务抛出未捕获异常后通常不会继续调度,生产代码要明确处理异常。

java
// 说明:ScheduledExecutorService:延迟或周期调度。
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ScheduledExecutorDemo {
    public static void main(String[] args) throws InterruptedException {
        ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
// 初始状态:scheduler = Executors.newScheduledThreadPool(1)。
// 作用:ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);;延迟或周期调度。
        try {
            scheduler.schedule(() -> {
// 作用:scheduler.schedule(() -> {;延迟或周期调度。
                System.out.println("delayed");
// 输出:delayed
            }, 10, TimeUnit.MILLISECONDS);
            scheduler.shutdown();
// 作用:scheduler.shutdown();;延迟或周期调度。
            scheduler.awaitTermination(1, TimeUnit.SECONDS);
// 作用:scheduler.awaitTermination(1, TimeUnit.SECONDS);;延迟或周期调度。
        } finally {
            scheduler.shutdownNow();
// 作用:scheduler.shutdownNow();;延迟或周期调度。
        }
    }
}

有序关闭执行器:拒绝新任务并等待收尾 ​

先 shutdown() 停止接收新任务,再等待已有任务;超时后用 shutdownNow() 请求中断。

java
// 说明:有序关闭执行器:拒绝新任务并等待收尾。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class ExecutorShutdownDemo {
    static void close(ExecutorService executor) throws InterruptedException {
        executor.shutdown();
// 初始状态:executor.shutdown()。
// 作用:executor.shutdown();;拒绝新任务并等待收尾。
        if (!executor.awaitTermination(1, TimeUnit.SECONDS)) {
            executor.shutdownNow();
// 初始状态:executor.shutdownNow()。
// 作用:executor.shutdownNow();;拒绝新任务并等待收尾。
            if (!executor.awaitTermination(1, TimeUnit.SECONDS)) {
                throw new IllegalStateException("executor did not stop");
// 初始状态:throw new IllegalStateException("executor did not stop")。
// 作用:throw new IllegalStateException("executor did not stop");;拒绝新任务并等待收尾。
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executor = Executors.newFixedThreadPool(1);
// 初始状态:executor = Executors.newFixedThreadPool(1)。
// 作用:ExecutorService executor = Executors.newFixedThreadPool(1);;拒绝新任务并等待收尾。
        close(executor);
        System.out.println("closed=" + executor.isTerminated());
// 输出:closed=true
    }
}

无论任务是否响应中断,都应该记录未完成任务并避免静默丢失。

不常用但需要知道 ​

Thread.UncaughtExceptionHandler:观察 execute 任务异常 ​

这是最后一道异常观察边界,不替代任务内部的业务错误处理。在线程池中应给线程设置统一 ThreadFactory,或者通过 Future 读取 submit 任务的异常。

java
// 作用:通过 Thread.UncaughtExceptionHandler 观察 execute 任务异常。
public class UncaughtExceptionDemo {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> { throw new IllegalStateException("bad task"); }, "worker");
// 初始状态:thread = new Thread(() -> { throw new IllegalStateException("bad task"); }, "worker")。
// 作用:Thread thread = new Thread(() -> { throw new IllegalStateException("bad task"); }, "worker");;观察 execute 任务异常。
        thread.setUncaughtExceptionHandler((t, error) -> {
// 作用:thread.setUncaughtExceptionHandler((t, error) -> {;观察 execute 任务异常。
            System.out.println(t.getName() + ":" + error.getClass().getSimpleName());
// 输出:worker:IllegalStateException
        });
        thread.start();
// 作用:thread.start();;观察 execute 任务异常。
        thread.join();
// 作用:thread.join();;观察 execute 任务异常。
    }
}

Thread.interrupt:协作式停止 ​

被阻塞在 sleep、wait 或 join 的线程通常会收到 InterruptedException;捕获后要么结束任务,要么恢复中断标记。吞掉中断会让上层取消和关闭策略失效。

java
// 作用:通过 Thread.interrupt 协作式停止。
public class InterruptDemo {
    public static void main(String[] args) throws InterruptedException {
        Thread worker = new Thread(() -> {
// 作用:Thread worker = new Thread(() -> {;协作式停止。
// 初始状态:worker = new Thread(() ->。
            try {
                Thread.sleep(10_000);
// 作用:Thread.sleep(10_000);;协作式停止。
            } catch (InterruptedException ex) {
                Thread.currentThread().interrupt();
// 初始状态:Thread.currentThread().interrupt()。
// 作用:Thread.currentThread().interrupt();;协作式停止。
                System.out.println("cancelled");
// 输出:cancelled
            }
        });
        worker.start();
// 作用:worker.start();;协作式停止。
        worker.interrupt();
// 初始状态:worker.interrupt()。
// 作用:worker.interrupt();;协作式停止。
        worker.join();
// 作用:worker.join();;协作式停止。
    }
}

取消任务:发出请求并检查取消状态 ​

任务尚未开始时 cancel(false) 可以取消它;任务已经运行时,cancel(true) 只是发出中断请求,是否停止取决于任务是否正确响应中断。

java
import java.util.concurrent.FutureTask;

public class FutureCancelDemo {
    public static void main(String[] args) {
        FutureTask<String> task = new FutureTask<>(() -> "never started");
// 初始状态:task = new FutureTask<>(() -> "never started")。
// 作用:FutureTask<String> task = new FutureTask<>(() -> "never started");;发出请求并检查取消状态。
        boolean cancelled = task.cancel(false);
// 初始状态:cancelled = task.cancel(false)。
// 作用:boolean cancelled = task.cancel(false);;发出请求并检查取消状态。
        System.out.println(cancelled + ", " + task.isCancelled());
// 输出:true, true
    }
}

Thread.State:诊断线程状态 ​

状态是诊断快照,不是业务同步工具。不要通过轮询 getState() 判断业务结果,应该使用 Future、CountDownLatch 或明确的消息协议。

java
// 作用:通过 Thread.State 诊断线程状态。
public class ThreadStateDemo {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> { });
// 初始状态:thread = new Thread(() -> { })。
// 作用:Thread thread = new Thread(() -> { });;诊断线程状态。
        System.out.println(thread.getState());
// 输出:NEW
        thread.start();
// 作用:thread.start();;诊断线程状态。
        thread.join();
// 作用:thread.join();;诊断线程状态。
        System.out.println(thread.getState());
// 输出:TERMINATED
    }
}

简单案例 ​

java
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadBasicsDemo {
    public static void main(String[] args) throws Exception {
        ExecutorService executor = Executors.newFixedThreadPool(2);
// 初始状态:executor = Executors.newFixedThreadPool(2)。
// 作用:ExecutorService executor = Executors.newFixedThreadPool(2);;诊断线程状态。
        try {
            var futures = executor.invokeAll(List.of(
                    () -> "java",
                    () -> "concurrency"));
// 作用:var futures = executor.invokeAll(List.of(;诊断线程状态。
// 初始状态:futures = executor.invokeAll(List.of(。
            System.out.println(futures.get(0).get() + " " + futures.get(1).get());
// 输出:java concurrency
        } finally {
            executor.shutdown();
// 作用:executor.shutdown();;诊断线程状态。
        }
    }
}

invokeAll 适合需要等一批 Callable 都返回的简单场景;若需要逐个完成就处理 CompletionService,若只要最快结果则考虑 invokeAny。

易混点 ​

  • start() 与 run() 不同:前者启动新线程,后者只是普通方法调用。
  • submit 会把任务异常放进 Future,必须调用 get() 或检查状态;execute 的异常走线程异常处理器。
  • shutdown() 不会中断正在运行的任务;shutdownNow() 也只是协作式中断请求。
  • sleep 不释放锁,wait 会释放当前对象监视器;两者用途不能互换。
  • 线程池大小、队列容量和拒绝策略要结合 CPU、阻塞比例和下游容量设置,不能只追求线程数。

课后小问 ​

  1. 为什么直接调用 thread.run() 看不到新线程效果? 答案:run() 是普通方法调用,代码仍在当前线程执行。 解析:只有 start() 才会让 JVM 调度新的线程,并建立独立的线程栈。

  2. Future 超时后任务一定停止了吗? 答案:不一定,get(timeout) 只停止等待,任务可能仍在执行。 解析:若要请求取消,需要显式调用 cancel(true),任务还必须响应中断。

  3. 为什么执行器必须关闭? 答案:线程池中的工作线程和队列属于资源,未关闭可能让应用或测试进程无法退出。 解析:shutdown 给出有序结束边界,超时后再用 shutdownNow 做最后的协作式停止。

本节小结 ​

  • Runnable/Callable 描述任务,Thread 和 ExecutorService 提供执行资源。
  • Future 负责结果、异常、取消和超时的观察,不会自动解决共享状态安全。
  • 使用执行器时要明确线程池容量、任务异常、中断响应和关闭流程。
  • 需要等待、批量提交或调度时优先使用对应的 Future、invokeAll 和 ScheduledExecutorService。

快速回顾 ​

  • 会用 Thread、Runnable 和 join() 启动并等待线程。
  • 会区分 execute、submit、invokeAll 与 invokeAny。
  • 知道中断是协作式取消,并能正确恢复中断标记。
  • 能写出 shutdown、等待、超时后 shutdownNow 的关闭流程。