并发主线先从线程怎样执行、等待和结束开始。线程池、锁和异步框架都建立在这些基础上。
怎样创建并执行任务?
可以继承 Thread,或向 Thread 提供 Runnable。业务通常把任务内容与线程管理分开:Runnable 没有返回值,Callable 可以返回结果并抛检查型异常,常通过 ExecutorService 提交。
Thread worker = new Thread(() -> System.out.println("running"));worker.start();start 让新线程参与调度并执行 run;直接调用 run 只是当前线程上的普通方法调用。同一个 Thread 对象不能重复 start。
六种线程状态
| 状态 | 含义与典型场景 |
|---|---|
| NEW | 创建后尚未启动 |
| RUNNABLE | 可运行,包括运行中或等待 CPU,也可能处于某些底层 IO 等待 |
| BLOCKED | 等待进入 synchronized 的监视器锁 |
| WAITING | 无超时等待,如 wait、join、park |
| TIMED_WAITING | 有超时等待,如 sleep、带超时的 wait/join |
| TERMINATED | 执行结束 |
这是 Java 层状态,不等于操作系统线程状态的逐项映射。ReentrantLock 竞争时可能通过 park 等待,不能统一说成 BLOCKED。
sleep、wait、join
| 方法 | 用途 | 锁与使用要求 |
|---|---|---|
| Thread.sleep | 当前线程暂停一段时间 | 不释放已持有的监视器锁 |
| Object.wait | 等待条件变化 | 必须持有该对象监视器;等待时释放这个监视器,返回前重新获取 |
| Thread.join | 当前线程等待目标线程结束 | 用于完成协作 |
wait 不释放线程持有的所有其他锁。notify/notifyAll 唤醒等待线程,但不会立刻交出锁;等待线程仍需要重新竞争。
等待条件要用 while 检查,既考虑虚假唤醒,也考虑重新拿到锁时条件已被其他线程改变。
synchronized (monitor) { while (!ready) { monitor.wait(); } // 使用相同锁保护下的条件}修改 ready 和发出通知也要遵守同一个监视器协议。
interrupt 会强制结束线程吗?
不会,中断是协作式请求。线程需要检查中断标记,或在可中断阻塞方法中收到 InterruptedException 后进行清理和退出。
isInterrupted 查询标记而不清除;静态 Thread.interrupted 查询当前线程并清除标记。sleep/wait/join 因中断抛出 InterruptedException 时通常会清除标记。
try { Thread.sleep(1000);} catch (InterruptedException e) { Thread.currentThread().interrupt(); return;}不能继续向上抛异常时,通常恢复标记并结束当前工作,不要吞掉中断。并非所有 IO 或任务都会及时响应中断,取消要看具体 API。
面试回答模板
Runnable/Callable 定义任务,Thread 或线程池负责执行。start 启动新线程,run 是普通方法调用。BLOCKED 特指等待监视器锁。sleep 不释放锁,wait 要在同步块中调用并释放对应监视器,join 等待线程结束。interrupt 是协作式取消请求,任务必须正确处理中断。
自测
- notify 后等待线程会立即运行吗?不保证,还要竞争锁并获得调度。
- wait 为什么放在 while 中?醒来后必须重新检查条件。
- 中断能让忽略中断的死循环自动退出吗?不能。