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:线程已经结束