作者:Brian Goetz

本书结构:

简要的介绍:第1章

基础知识:第一部分(第 2 章 ~ 第 5 章)

结构化并发应用程序:第二部分(第 6 章 ~ 第 9 章)

活跃性、性能与测试:第三部分(第 10 章 ~ 第 12 章)

高级主题:第四部分(第 13 章 ~ 第 16 章)

第 1 章 简介

1. 线程的优势

  • 发挥多处理器的强大能力

  • 建模的简单性

  • 异步事件的简化处理

  • 响应更灵敏的用户界面

2. 线程带来的风险

  • 安全性问题

  • 活跃性问题

  • 性能问题

第一部分 基础知识

第 2 章 线程安全性

1. 什么是线程安全

当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些线程将如何交替执行,并且在主调代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么就称这个类是线程安全的。

无状态对象一定是线程安全的。

2. 原子性

在并发编程中,由于不恰当的执行时序而出现不正确的结果是一种非常严重的情况,它有一个正式的名字:竞态条件(Race Condition)。

示例1:读取 - 修改 - 写入

@NotThreadSafe
public class UnsafeCountingFactorizer implement Servlet {
    private long count = 0;

    public lo ng getCount() { return count; }

    public void service (ServletRequest req, ServletResponse resp) {
        BigInteger i = extractFromRequest(req);
        BigInteger[] factors = factor(i);
        ++count;
        encodeIntoResponse(resp, factors);
    }
}

示例2:延迟初始化中的竞态条件

NotThreadSafe
public class LazyInitRace {
    private ExpensiveObject instance = null;

    public ExpensiveObject getInstance() {
        if (instance == null) {
            instance = new ExpensiveObject();
        }
    }
}

原子操作是指,对于访问同一个状态的所有操作(包括该操作本身)来说,这个操作是一个以原子方式执行的操作。

在无状态的类中添加一个状态时,如果这个状态完全由线程安全的对象来管理,那么这个类仍然是线程安全的。然而,当状态变量的数量由一个变为多个时,就不那么简单了。

@ThreadSafe
public class CountingFactorizer implement Servlet {
    private final AtomicLong count = new AtomicLong(0);

    public lo ng getCount() { return count; }

    public void service (ServletRequest req, ServletResponse resp) {
        BigInteger i = extractFromRequest(req);
        BigInteger[] factors = factor(i);
        count.incrementAndGet();
        encodeIntoResponse(resp, factors);
    }
}

在实际情况中,应尽可能的使用现有的线程安全对象(例如 AtomicLong)来管理类的状态。

3. 加锁机制