1. 多线程编程
概念
线程是进程内的调度单位,共享进程地址空间。分布式服务里,一个进程通常用多个线程同时接受连接、执行 RPC 或等待 I/O。 创建线程只是把任务交给调度器,线程真正跑起来要等 start。运行中可以主动休眠一段时间,也可以在条件不满足时等待,直到别的线程唤起它。 Java 的 Thread.sleep 与 Python 的 time.sleep 只暂停当前线程,不释放它已经拿到的锁。要在等待期间把锁让出去,Java 用 Object.wait,Python 用 Condition.wait 或 Event.wait。唤起对应 notify / notifyAll 或 Event.set。
- 创建:Java 用 Thread 或 Runnable,Python 用 threading.Thread。调用 start 之后线程才进入可调度状态。
- 状态:Java 有 NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED。BLOCKED 是在等别人持有的锁;WAITING 是 wait 之后等人唤起;TIMED_WAITING 是 sleep 或限时 wait。
- 休眠:sleep 到期自己继续,不释放 synchronized / Lock。
- 唤起:必须由另一个线程 notify、notifyAll 或 set Event。只改布尔标志而不唤起,等待方会一直停着。
- wait 必须放在锁里,并且用 while 重新检查条件,避免虚假唤醒。
算法流程
下面的 UML 图说明 Java 例程和 Python 例程共同实现的流程。实线箭头表示调用或发送,虚线箭头表示返回或唤起。
线程状态(UML 状态图)
stateDiagram-v2
[*] --> NEW: 构造线程对象
NEW --> RUNNABLE: start
RUNNABLE --> TIMED_WAITING: sleep
TIMED_WAITING --> RUNNABLE: 到期
RUNNABLE --> WAITING: wait
WAITING --> RUNNABLE: notify
RUNNABLE --> BLOCKED: 等锁
BLOCKED --> RUNNABLE: 获锁
RUNNABLE --> TERMINATED: 结束
TERMINATED --> [*]
休眠与唤起(UML 时序图)
sequenceDiagram
participant Main as 主线程
participant Worker as worker
participant Gate as 条件门闩
Main->>Worker: start
Worker->>Gate: await
Gate-->>Worker: 条件未满足,wait 并暂时释放锁
Main->>Main: sleep,不释放已持有的锁
Main->>Gate: open,先改条件再 notifyAll
Gate-->>Worker: 唤起后用 while 重新检查
Worker-->>Main: 条件已满足,继续执行
Main->>Worker: join,等到线程结束
Java 例程
public class ThreadBasics {
/** 条件门闩:worker 在门开之前等待,主线程负责打开。 */
static class Gate {
private final Object lock = new Object(); // 等待和唤起必须用同一把锁
private boolean ready = false; // 条件本身,不能只靠 notify
void await() throws InterruptedException {
synchronized (lock) { // wait 必须先持有 lock
while (!ready) { // while:被唤醒后重新检查,防止虚假唤醒
// 进入 WAITING,并暂时释放 lock,让 open() 能进来
lock.wait();
}
} // 离开 synchronized 时再次释放 lock
}
void open() {
synchronized (lock) { // notify 也必须持有同一把锁
ready = true; // 先改条件,再唤起
lock.notifyAll(); // 唤醒所有在 lock 上等待的线程
}
}
}
public static void main(String[] args) throws Exception {
Gate gate = new Gate();
// 只构造线程对象,此时状态是 NEW,run() 还没有执行
Thread worker = new Thread(new Runnable() {
public void run() {
try {
gate.await(); // 条件不满足就停在这里
System.out.println("worker awakened");
} catch (InterruptedException ex) {
Thread.currentThread().interrupt(); // 恢复中断标记,交给上层处理
}
}
}, "worker");
System.out.println("before start: " + worker.getState()); // 期望 NEW
worker.start(); // 之后才变成可调度,不要直接调用 run()
Thread.sleep(200); // 主线程进入 TIMED_WAITING;sleep 不释放已持有的锁
System.out.println("after sleep: " + worker.getState()); // worker 多半已是 WAITING
gate.open(); // 另一个线程负责唤起
worker.join(); // 主线程等到 worker 结束
System.out.println("after join: " + worker.getState()); // 期望 TERMINATED
}
}
Python 例程
import threading
import time
# Event 是一个线程安全的开关。clear 是关,set 是开,wait 会停到开关被打开。
ready = threading.Event()
def worker():
print("worker waiting")
ready.wait() # 条件不满足时挂起;别的线程 set 之后从这里继续
print("worker awakened", threading.current_thread().name)
# target 是线程要执行的函数。此时线程对象已创建,但还没开始跑。
t = threading.Thread(target=worker, name="worker")
print("before start, alive =", t.is_alive()) # False:尚未 start
t.start() # 交给调度器,worker 才会执行
time.sleep(0.2) # 当前线程休眠 0.2 秒。sleep 不会释放已持有的 Lock
print("while waiting, alive =", t.is_alive()) # True:worker 正停在 wait
ready.set() # 唤起所有正在 wait 这个 Event 的线程
t.join() # 等 worker 函数返回
print("after join, alive =", t.is_alive()) # False:线程已经结束