1Seven Concurrency Models in Seven Weeks

线程与锁

传统并发

线程锁条件变量死锁

🎯 AI 时代的学习建议:不要只读理论。理解这一章的设计动机(为什么需要这个概念?)和边界条件(什么情况下会失效?)。当 AI 生成的代码涉及这些概念时,你能判断它是否用对了场景。AI 能帮你总结内容,但无法替代你亲手阅读、思考、建立直觉的过程。

第1章:线程与锁(Threads and Locks)

一、导读

1.1 本章学习目标

第1章介绍最经典的并发模型——线程与锁。这是 Java、C++、Python 等语言中最常见的并发方式,也是最容易出错的。

本章的核心学习目标包括:

  • 理解线程的基本概念:创建、同步、通信
  • 掌握锁的使用:互斥锁、读写锁、条件变量
  • 理解死锁的产生条件和预防方法
  • 认识线程与锁模型的固有缺陷

1.2 为什么本章如此重要

线程与锁是并发编程的"原始模型"。理解它的优势和局限,才能理解为什么后续章节要介绍其他并发模型。大多数并发 bug——数据竞争、死锁、活锁、饥饿——都源于对锁的不当使用。

1.3 核心问题引导

为什么线程与锁模型如此难以正确使用?

死锁的四个必要条件是什么?如何预防?

锁的粒度如何影响性能和正确性?

为什么有人说"线程与锁是并发的汇编语言"?


二、核心概念详解

2.1 线程基础

java
// Java 线程
class Counter {
    private int count = 0;
    
    public synchronized void increment() {
        count++;
    }
    
    public synchronized int getCount() {
        return count;
    }
}

// 创建线程
Thread t = new Thread(() -> {
    for (int i = 0; i < 1000; i++) {
        counter.increment();
    }
});
t.start();
t.join();

2.2 锁与同步

java
// synchronized 关键字
public synchronized void transfer(Account from, Account to, int amount) {
    from.withdraw(amount);
    to.deposit(amount);
}

// ReentrantLock
private final Lock lock = new ReentrantLock();

public void criticalSection() {
    lock.lock();
    try {
        // 临界区
    } finally {
        lock.unlock();
    }
}

// 条件变量
private final Lock lock = new ReentrantLock();
private final Condition notFull = lock.newCondition();
private final Condition notEmpty = lock.newCondition();

2.3 死锁

java
// 经典死锁场景
class Account {
    private int balance;
    private final Lock lock = new ReentrantLock();
    
    // 危险!不同顺序获取锁导致死锁
    public void transfer(Account other, int amount) {
        lock.lock();
        try {
            other.lock.lock();  // 可能死锁!
            try {
                this.balance -= amount;
                other.balance += amount;
            } finally {
                other.lock.unlock();
            }
        } finally {
            lock.unlock();
        }
    }
}

// 解决方案:锁排序
public void transfer(Account other, int amount) {
    Account first = this.id < other.id ? this : other;
    Account second = this.id < other.id ? other : this;
    
    first.lock.lock();
    try {
        second.lock.lock();
        try {
            this.balance -= amount;
            other.balance += amount;
        } finally {
            second.lock.unlock();
        }
    } finally {
        first.lock.unlock();
    }
}

2.4 线程安全集合

java
// ConcurrentHashMap
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
map.put("key", 1);
map.computeIfAbsent("key", k -> 0);

// BlockingQueue
BlockingQueue<Task> queue = new LinkedBlockingQueue<>();

// 生产者
queue.put(new Task("task1"));

// 消费者
Task task = queue.take();  // 阻塞等待

三、实战案例

3.1 案例一:生产者-消费者模式

java
class ProducerConsumer {
    private final BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);
    
    class Producer implements Runnable {
        public void run() {
            try {
                for (int i = 0; i < 100; i++) {
                    queue.put(i);
                    Thread.sleep(10);
                }
                queue.put(-1);  // 结束信号
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }
    
    class Consumer implements Runnable {
        public void run() {
            try {
                while (true) {
                    int item = queue.take();
                    if (item == -1) break;
                    System.out.println("Consumed: " + item);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }
}

3.2 案例二:读写锁优化

java
class ReadWriteCache {
    private final Map<String, String> cache = new HashMap<>();
    private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
    
    public String get(String key) {
        rwLock.readLock().lock();
        try {
            return cache.get(key);
        } finally {
            rwLock.readLock().unlock();
        }
    }
    
    public void put(String key, String value) {
        rwLock.writeLock().lock();
        try {
            cache.put(key, value);
        } finally {
            rwLock.writeLock().unlock();
        }
    }
}

四、本章小结

线程与锁是最基础的并发模型,但容易出错。

死锁的四个必要条件:互斥、持有并等待、不可抢占、循环等待。

锁的粒度影响性能和正确性——太粗影响并发度,太细增加开销。

线程与锁模型被称为"并发的汇编语言"——强大但难以直接使用。


五、思考与练习

为什么线程与锁模型如此难以正确使用?根本原因是什么?

如何检测和预防死锁?有哪些工具和方法?

读写锁在什么场景下比互斥锁更合适?


1/7线程与锁
0%