01、前言

先让我吐一句肺腑之言吧,不说出来会憋出内伤的。《Java 并发编程实战》这本书太特么枯燥了,尽管它被奉为并发编程当中的经典之作,但我还是忍不住。因为第四章“对象的组合”我整整啃了两周的时间,才啃出来点肉丝。

读者朋友们见谅啊。要怪只能怪我自己的学习能力有限,真读不了这种生硬无趣的技术书。但是为了学习,为了进步,为了将来(口号喊得有点大了),只能硬着头皮上。

请随我来,我尽量写得有趣点。

02、线程安全类

作者说了啊,设计一个线程安全类需要三个步骤:

1)找出表示对象状态的所有变量
2)对变量进行有效性约束
3)增加类的并发访问策略

我在作者说的基础上做了微调,读起来更加容易理解。怎么和代码对应起来了,先来看一个普通的计数器类 Counter。

public class Counter {     private int value = 0;      public int getValue() {         return value;     }      public int increment() {         return ++value;     } }

1)Counter 的状态变量只有一个,就是 value。

2)value 的有效性是什么呢,它最大不能超过 Integer.MAX_VALUE,最小只能为 0(计数嘛,总不能记成负数)。换句话说就是,value 的有效范围是 0 ~ Integer.MAX_VALUE

public int increment() {     if (value == Integer.MAX_VALUE) {         throw new IllegalStateException("counter overflow");     }     return ++value; }

3)增加类的并发访问策略,直接上 synchronized。

public class Counter {     private int value = 0;      public synchronized int getValue() {         return value;     }      public synchronized int increment() {         if (value == Integer.MAX_VALUE) {             throw new IllegalStateException("counter overflow");         }         return ++value;     } }

03、非线程安全的对象

之前我们谈了如何设计一个线程安全的类。如果类是安全的,那么它作为对象使用的时候就是线程安全的。但如果一个类不是线程安全的,它作为对象使用的时候怎么保证是线程安全的呢?

作者提到了一个名词叫做“封闭机制”:

1)把对象作为类的私有成员变量;
2)把对象作为方法内部的局部变量;
3)线程 A 把对象传递到 B 线程,而不是与线程 B 共享这个对象;

大家来看下面这段代码。

class StringList {     private List<String> myList = new ArrayList<>();          public synchronized void addString(String s) {         myList.add(s);     }          public synchronized void removeString(String s) {         myList.remove(s);     } }

本身 ArrayList 不是线程安全的,但 myList 是私有的,访问它的两个方法 addString() 和 removeString() 都加了关键字