--- title: "01-线程基础与生命周期" created: 2026-09-01 tags: - Java --- # 线程基础与生命周期 > [!note] 本篇定位 > 并发的第一课:线程是什么、怎么创建、状态怎么流转、常用控制方法各干什么。读法:先看"进程 vs 线程"建立模型,再背熟**六种状态**(面试必考画图),最后过一遍创建四式与常用方法。深入讨论可见性/锁的进 02 篇。 ## 进程与线程 - **进程**:操作系统资源分配的基本单位,一个运行中的程序就是一个进程,拥有独立的内存空间; - **线程**:CPU 调度的基本单位,一个进程内可有多个线程,它们**共享进程的堆和方法区**,但各自持有独立的栈和程序计数器; - 线程共享堆意味着:一个线程改了对象字段,另一个线程立刻"能看见"(可见性问题就从这里来);线程私有栈意味着局部变量天然线程安全。 ```java public class ThreadDemo { public static void main(String[] args) { // main 方法本身运行在一个名为 main 的线程里 System.out.println(Thread.currentThread().getName()); // main } } ``` ## 创建线程的四种方式 ### 1. 继承 Thread ```java public class MyThread extends Thread { @Override public void run() { System.out.println("继承 Thread:" + Thread.currentThread().getName()); } } // 启动 new MyThread().start(); // 注意是 start(),不是 run() ``` ### 2. 实现 Runnable(推荐) ```java Runnable task = () -> System.out.println("Runnable:" + Thread.currentThread().getName()); new Thread(task).start(); ``` ### 3. Callable + FutureTask(有返回值、可抛异常) ```java Callable task = () -> { Thread.sleep(100); return 66; }; FutureTask ft = new FutureTask<>(task); new Thread(ft).start(); System.out.println(ft.get()); // 阻塞等结果:66 ``` ### 4. 线程池(生产环境标准姿势) ```java ExecutorService pool = new ThreadPoolExecutor( 2, 4, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(100), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy()); pool.execute(() -> System.out.println("线程池执行")); pool.shutdown(); ``` **面试要点**:为什么推荐 Runnable/线程池而不是继承 Thread?——① Java 单继承,继承了 Thread 就没法再继承别的类;② 任务与执行解耦,配合线程池复用线程(线程的创建销毁是重开销操作)。 ## 六种状态(Thread.State) ```text NEW 新建,还没调用 start() │ start() ▼ RUNNABLE 可运行(就绪 + 运行中,Java 把两者合并了) │ 等待锁 / wait() / sleep() / IO ... ▼ ┌────────────────────────────────────────────┐ │ BLOCKED 等待获取 synchronized 锁 │ │ WAITING wait()/join()/park() 无限等待 │ │ TIMED_WAITING sleep(n)/wait(n)/join(n) 限时 │ └────────────────────────────────────────────┘ │ 被唤醒 / 拿到锁 / 超时 ▼ TERMINATED run() 执行完毕 ``` **面试高频**:`sleep()` vs `wait()`——sleep 是 Thread 静态方法,**抱着锁睡**(不释放锁);wait 是 Object 方法,**必须先持有锁才能调**,调用后释放锁进入 WAITING,靠 notify/notifyAll 唤醒。 ## 常用控制方法 ```java Thread t = new Thread(() -> { for (int i = 0; i < 5; i++) { System.out.println("working " + i); } }); t.start(); t.join(); // main 阻塞等 t 结束(常用于"等子线程跑完再继续") Thread.sleep(100); // 当前线程限时休眠,不释放锁,抛 InterruptedException Thread.yield(); // 提示调度器"我愿意让出 CPU",纯建议,不保证 t.setDaemon(true); // 守护线程(必须在 start 前设置):所有用户线程结束后 JVM 不等守护线程直接退出 ``` ## 中断(interrupt):协作式,不是强杀 ```java Thread worker = new Thread(() -> { while (!Thread.currentThread().isInterrupted()) { // 干活…… } System.out.println("收到中断信号,优雅退出"); }); worker.start(); Thread.sleep(50); worker.interrupt(); // 只是设置标志位,不会立刻停 ``` - `interrupt()` 只是把中断标志置 true;`isInterrupted()` 查询;`Thread.interrupted()` 查询并**清除**; - 线程在 `sleep()/wait()/join()` 中被中断会抛 `InterruptedException` 并**清除**标志——捕获后通常要再 `interrupt()` 自己或直接 return; - **Java 没有"强制杀死线程"的安全手段**,stop()/suspend() 早已废弃(可能造成数据不一致),中断是纯协作式的。 ## 常见坑 - `start()` 只能调一次,第二次抛 `IllegalThreadStateException`; - 守护线程里 finally 不保证执行(JVM 退出不等守护线程); - 别在 run() 里 catch 住所有异常不处理——线程静默死亡,用 `UncaughtExceptionHandler` 或线程池的 future.get() 拿异常。 --- ⬅️ [[00-Java并发总览|00-Java并发总览]] 🏠 [[00-Java|00-Java]] ➡️ [[02-synchronized与volatile|02-synchronized与volatile]]