Java并发编程实战:第4章 组合对象

1. 设计线程安全的类

尽管将所有的状态都存储在公共静态域中,仍然能写出线程安全的程序。

在没有进行全局检查的情况下,封装能够保证类的线程安全性。

设计线程安全类的过程应该包括下面3个基本要素:

  • 确定对象状态是由哪些变量构成的;
  • 确定限制状态变量的不变约束;
  • 制定一个管理并发访问对象状态的策略。

1.1 同步策略(Synchronization Policy)

定义了对象如何协调对其状态的访问,并且不会违反它的不便约束或验证条件。

使用 Java 监视器模式的简单线程安全计数器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import net.jcip.annotations.GuardedBy;
import net.jcip.annotations.ThreadSafe;

/**
* Created by osys on 2022/08/28 21:48.
*/
@ThreadSafe
public final class Counter {
@GuardedBy("this")
private long value = 0;

public synchronized long getValue() {
return value;
}

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

1.2 收集同步需求

对象与变量拥有一个 状态空间 :这个空间即 它们可能处于状态的范围

例如上面的:Counter

1
2
3
4
5
6
7
8
// long 类型数据限制其范围是 Long.MIN_VALUE 到 Long.MAX_VALUE
private long value = 0; // 限制了 value 的值必须是正值


if (value == Long.MAX_VALUE) { // 是否非法
throw new IllegalStateException("counter overflow");
}
return ++value;

类似地,操作的后验条件会指出某种 状态转换(state transitions) 是非法的

不变约束后验条件 施加在状态及状态转换上的约束,引入了额外的同步与封装的需要。

1.3 状态依赖的操作

  • 类的 不变约束 与方法的 后验条件 约束了对象 合法的状态合法状态转换
  • 对象的方法也可以有 先验条件。如无法从空队列中移除一个条目。
  • 单线程中,操作如果无法满足 先验条件 必然失败。
  • 多线程中,原本为 的先验条件,可能会由于其它线程的活动变为

在并发程序中,有持续等待, 直到先验条件为真,再继续处理的操作。在 Java 中,等待特定条件成立的内置高效机制 waitnotify 与内部锁紧密地绑定在一起。

1.4 状态所有权

  • 在很多情况下,所有权封裝性总是在一起出现的。

  • 对象封装它拥有的状态,且拥有它封装的状态。拥有给定状态的所有者决定了锁协议,该协议用于维护变量状态的完整性。

  • 所有权意味着控制权,不过一旦将引用发布到一个可变对象上,就不再拥有独占的控制权,充其量只可能有共享控制权

  • 类通常不会拥有由构造函数或方法传递进来的对象,除非该方法是被明确设计用来转换传递对象的所有权的(比如同步容器的包装工厂方法)。

2. 实例限制

2.1 实例限制

即使一个对象不是线程安全的,仍然有许多技术可以让它安全地用于多线程程序。

比如,你可以确保它只被单一线程访问(线程限制),也可以确保所有的访问都正确地被锁保护

通过使用实例限制 (instance confinement),封装简化了类的线程安全化工作,这通常称为限制

将数据封装在对象内部,把对数据的访问限制在对象的方法上,更易确保线程在访问数据时总能获得正确的锁。

使用限制确保线程安全:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import net.jcip.annotations.GuardedBy;
import net.jcip.annotations.ThreadSafe;

import java.util.HashSet;
import java.util.Set;

/**
* Created by osys on 2022/08/28 21:48.
*/
@ThreadSafe
public class PersonSet {
@GuardedBy("this")
private final Set<Person> mySet = new HashSet<>();

public synchronized void addPerson(Person p) {
mySet.add(p);
}

public synchronized boolean containsPerson(Person p) {
return mySet.contains(p);
}

interface Person {
}
}

2.2 Java 监视器模式(Java monitor pattern)

  • 线程限制原则的直接推论之一是Java 监视器模式

  • 遵循Java监视器模式的对象封裝了所有的可变状态,并由对象自己的内部锁保护。

  • Java的内置锁有时也被叫做监视器锁(monitor locks) 或监视器(monitors)

  • Counter 演示了这个模式的典型案例:

    Counter 封裝了一个状态变量value,所有对该变最的访问都要通过 Counter 的方法,这些方法都是同步的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    import net.jcip.annotations.GuardedBy;
    import net.jcip.annotations.ThreadSafe;

    @ThreadSafe
    public final class Counter {
    @GuardedBy("this")
    private long value = 0;

    public synchronized long getValue() {
    return value;
    }

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

私有锁保护状态:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import apple.laf.JRSUIConstants;
import net.jcip.annotations.GuardedBy;

/**
* Created by osys on 2022/08/28 21:48.
*/
public class PrivateLock {

private final Object myLock = new Object();

@GuardedBy("myLock")
JRSUIConstants.Widget widget;

void someMethod() {
synchronized (myLock) {
// 访问或修改 widget 的状态
}
}
}
  • 使用私有锁对象,而不是对象的内部锁(或任何其他可公共访问的锁),有很多好处。
  • 私有的锁对象可以封装锁,这样客户代码无法得到它。
  • 可公共访问的锁允许客户代码涉足它的同步策略 —- 正确地或不正确地。
    • 客户不正确地得到另一个对象的锁,会引起活跃度方面的问题。
    • 另外要验证程序是正确地使用着一个可公共访问的锁,需要检查完整的程序,而不是一个单独的类。

2.3 机动车追踪器(tracking fleet vehicle)

每一辆机动车都有一个 string 标识,并有一个与之对应的位置(x,y)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
import net.jcip.annotations.ThreadSafe;

import java.awt.Point;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
* 机动车追踪器
*/
@ThreadSafe
public class DelegatingVehicleTracker {
/** 机动车位置 */
private ConcurrentMap<String, Point> locations;
/** 机动车位置(不可修改的,即最初位置) */
private final Map<String, Point> unmodifiableMap;

public DelegatingVehicleTracker(Map<String, Point> points) {
locations = new ConcurrentHashMap<String, Point>(points);
unmodifiableMap = Collections.unmodifiableMap(locations);
}

/** 所有机动车位置(不可变的) */
public Map<String, Point> getLocations() {
return unmodifiableMap;
}

/**
* 某机动车位置
* @param id 机动车 id
* @return 机动车位置
*/
public Point getLocation(String id) {
return locations.get(id);
}

/**
* 修改机动车位置(需要存在该机动车)
* @param id 机动车 id
* @param x 机动车 x 坐标
* @param y 机动车 y 坐标
*/
public void setLocation(String id, int x, int y) {
if (locations.replace(id, new Point(x, y)) == null) {
throw new IllegalArgumentException("车辆名称无效: " + id);
}
}

/**
* 机动车移动
* @param evt 机动车移动事件
*/
public void vehicleMoved(VehicleMovedEvent evt) {
// 位置
Point local = evt.point;
// 修改机动车位置(需要存在该机动车)
setLocation(evt.vehicleId, local.x, local.y);
}

/** 所有机动车位置(可变的) */
public Map<String, Point> getLocationsAsStatic() {
return Collections.unmodifiableMap(
new HashMap<String, Point>(locations));
}

/**
* 打印机动车位置
* @param vehicleId 机动车id
* @param local 机动车位置
*/
public void renderVehicle(String vehicleId, Point local) {
System.out.println("机动车 " + vehicleId + " 位置:" + local);
}

/** 机动车移动事件 */
static class VehicleMovedEvent {
Point point;
String vehicleId;

public VehicleMovedEvent(Point point, String vehicleId) {
this.point = point;
this.vehicleId = vehicleId;
}
}
}

3. 委托线程安全

3.1 基于监视器的机动车追踪器实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import net.jcip.annotations.GuardedBy;
import net.jcip.annotations.ThreadSafe;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
* 基于监视器的机动车追踪器实现
*/
@ThreadSafe
public class MonitorVehicleTracker {
@GuardedBy("this")
private final Map<String, MutablePoint> locations;

public MonitorVehicleTracker(Map<String, MutablePoint> locations) {
this.locations = deepCopy(locations);
}

public synchronized Map<String, MutablePoint> getLocations() {
return deepCopy(locations);
}

public synchronized MutablePoint getLocation(String id) {
MutablePoint loc = locations.get(id);
return loc == null ? null : new MutablePoint(loc);
}

public synchronized void setLocation(String id, int x, int y) {
MutablePoint loc = locations.get(id);
if (loc == null) {
throw new IllegalArgumentException("No such ID: " + id);
}
loc.x = x;
loc.y = y;
}

/**
* 深拷贝一个机动车追踪器
* @param m 机动车追踪器
* @return 深拷贝后的机动车追踪器
*/
private static Map<String, MutablePoint> deepCopy(Map<String, MutablePoint> m) {
Map<String, MutablePoint> result = new HashMap<>(m.size());
for (String id : m.keySet()) {
result.put(id, new MutablePoint(m.get(id)));
}
// 深拷贝后,返回一个不可修改的 Map
return Collections.unmodifiableMap(result);
}
}

3.2 将现场安全委托到 ConcurrentHashMap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
import net.jcip.annotations.ThreadSafe;

import java.awt.Point;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
* 机动车追踪器
*/
@ThreadSafe
public class DelegatingVehicleTracker {
/** 机动车位置 */
private ConcurrentMap<String, Point> locations;
/** 机动车位置(不可修改的,即最初位置) */
private final Map<String, Point> unmodifiableMap;

public DelegatingVehicleTracker(Map<String, Point> points) {
locations = new ConcurrentHashMap<String, Point>(points);
unmodifiableMap = Collections.unmodifiableMap(locations);
}

/** 所有机动车位置(不可变的) */
public Map<String, Point> getLocations() {
return unmodifiableMap;
}

/**
* 某机动车位置
* @param id 机动车 id
* @return 机动车位置
*/
public Point getLocation(String id) {
return locations.get(id);
}

/**
* 修改机动车位置(需要存在该机动车)
* @param id 机动车 id
* @param x 机动车 x 坐标
* @param y 机动车 y 坐标
*/
public void setLocation(String id, int x, int y) {
if (locations.replace(id, new Point(x, y)) == null) {
throw new IllegalArgumentException("车辆名称无效: " + id);
}
}

/**
* 机动车移动
* @param evt 机动车移动事件
*/
public void vehicleMoved(VehicleMovedEvent evt) {
// 位置
Point local = evt.point;
// 修改机动车位置(需要存在该机动车)
setLocation(evt.vehicleId, local.x, local.y);
}

/** 所有机动车位置(可变的) */
public Map<String, Point> getLocationsAsStatic() {
return Collections.unmodifiableMap(
new HashMap<String, Point>(locations));
}

/**
* 打印机动车位置
* @param vehicleId 机动车id
* @param local 机动车位置
*/
public void renderVehicle(String vehicleId, Point local) {
System.out.println("机动车 " + vehicleId + " 位置:" + local);
}

/** 机动车移动事件 */
static class VehicleMovedEvent {
Point point;
String vehicleId;

public VehicleMovedEvent(Point point, String vehicleId) {
this.point = point;
this.vehicleId = vehicleId;
}
}
}

3.3 非状态依赖变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import java.awt.event.KeyListener;
import java.awt.event.MouseListener;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

/**
* 委托线程安全到多个底层的状态变量
*/
public class VisualComponent {
private final List<KeyListener> keyListeners = new CopyOnWriteArrayList<>();
private final List<MouseListener> mouseListeners = new CopyOnWriteArrayList<>();

public void addKeyListener(KeyListener listener) {
keyListeners.add(listener);
}

public void addMouseListener(MouseListener listener) {
mouseListeners.add(listener);
}

public void removeKeyListener(KeyListener listener) {
keyListeners.remove(listener);
}

public void removeMouseListener(MouseListener listener) {
mouseListeners.remove(listener);
}
}

VisualComponent 使用 CopyOnWriteArrayList 存储每个监听器清单。

在 VisualComponent 中,不但每个 List 是线程安全的,而且不存在哪个不变约束会增加一个状态与另一个状态间的耦合,所以 VisualComponent 可以将它的线程安全贵任委托到 MouseListener 和 KeyListener 对象上。

3.4 不完整地保护不变约束

NumberRange 不是线程安全的;它没有保护好用于约東 lower 和 upper 的不变约束。

setLower 和 setupper 都是检查再运行的操作,但是它们没有适当地加锁以保证其原子性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import java.util.concurrent.atomic.AtomicInteger;

/**
* NumberRange 类没有完整地保护它的不变约束
*/
public class NumberRange {
/** 不变约束: lower <= upper */
private final AtomicInteger lower = new AtomicInteger(0);
private final AtomicInteger upper = new AtomicInteger(0);

/** 设置最低 number */
public void setLower(int i) {
// 警告 -- 不安全的 "检查再运行"
if (i > upper.get()) {
throw new IllegalArgumentException("can't set lower to " + i + " > upper");
}
lower.set(i);
}

/** 设置最高 number */
public void setUpper(int i) {
// 警告 -- 不安全的 "检查再运行"
if (i < lower.get()) {
throw new IllegalArgumentException("can't set upper to " + i + " < lower");
}
upper.set(i);
}

/**
* 判断 i 是否满足 lower <= i <= upper
* @return true or false
*/
public boolean isInRange(int i) {
return (i >= lower.get() && i <= upper.get());
}
}

如果类中还存在复合操作,如 setLower 和 setupper,类必须提供它自身有的锁以保证复合操作都是原子的。

除非所有的操作可以委托给一个状态变量。如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import java.util.concurrent.atomic.AtomicReference;

/**
* NumberRange2 类完整地保护它的不变约束
*/
public class NumberRange2 {
/** 不变约束: lower <= upper */
private final AtomicReference<RangeNum> lowerAndUpper = new AtomicReference<>(new RangeNum(0, 0));

/** 设置最低 number */
public void setLower(int i) {
lowerAndUpper.set(new RangeNum(i, lowerAndUpper.get().upper));
}

/** 设置最高 number */
public void setUpper(int i) {
lowerAndUpper.set(new RangeNum(lowerAndUpper.get().lower, i));
}

/**
* 判断 i 是否满足 lower <= i <= upper
* @return true or false
*/
public boolean isInRange(int i) {
return (i >= lowerAndUpper.get().lower && i <= lowerAndUpper.get().upper);
}

class RangeNum {
public int lower;
public int upper;
public RangeNum(int lower, int upper) {
// 警告 -- 不安全的 "检查再运行"
if (upper < lower) {throw new IllegalArgumentException("upper must be greater than or equal to lower!");}
this.lower = lower;
this.upper = upper;
}
}
}

3.5 发布底层的状态变量

如果一个状态变量是线程安金的,没有任何不变约束限制它的值,并且没有任何状态转换限制它的操作,那么它可以被安全发布。

发布了状态的机动车追踪器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
import net.jcip.annotations.GuardedBy;
import net.jcip.annotations.ThreadSafe;

import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
* Created by osys on 2022/08/28 21:48.
*/
@ThreadSafe
public class PublishingVehicleTracker {
private final Map<String, SafePoint> locations;
private final Map<String, SafePoint> unmodifiableMap;

public PublishingVehicleTracker(Map<String, SafePoint> locations) {
this.locations = new ConcurrentHashMap<>(locations);
this.unmodifiableMap = Collections.unmodifiableMap(this.locations);
}

public Map<String, SafePoint> getLocations() {
return unmodifiableMap;
}

public SafePoint getLocation(String id) {
return locations.get(id);
}

public void setLocation(String id, int x, int y) {
if (!locations.containsKey(id)) {
throw new IllegalArgumentException("invalid vehicle name: " + id);
}
locations.get(id).set(x, y);
}

@ThreadSafe
class SafePoint {
@GuardedBy("this")
private int x, y;

public SafePoint(int x, int y) {
this.set(x, y);
}

private SafePoint(int[] a) {
this(a[0], a[1]);
}

public SafePoint(SafePoint p) {
this(p.get());
}

public synchronized int[] get() {
return new int[]{x, y};
}

public synchronized void set(int x, int y) {
this.x = x;
this.y = y;
}
}

}
  • PublishingVehicleTracker 的线程安全性源自于它所委托的底层 ConcurrentHashMap。
  • 不过这次 Map 的内容是线程安全的可变 SafePoint,而非不可变的。
  • getLocation 方法返回底层 Map 的不可变拷贝,调用者在其上无法添加或移除车辆,却可以通过修改返回的 Map 中 SafePoint 的值,改变一个机动车的位置。
  • 只有 PublishingVehicleTracker 对机动车追踪器的合法值没有施加任何额外的约束时,它才是线程安全的。
  • 如果需要对机动车的 location 的改变(setLocation())进行判断或者执行一些其他的操作,那么 PublishingVehicleTracker 的做法可能就不正确了.

4. 向已有的线程安全类添加功能

4.1 向已有的线程安全类添加功能

  • Java 类库中包含了很多有用的 构建块 类。重用这些已有的类要好于创建一个新的。重用能够降低开发的难度、风和维护成本。

  • 有时一个线程安全类支持我们需要的全部操作,但更多时候,一个类只支持我们需要的大部分操作,这时我们需要在不破坏其线程安全性的前提下,向它添加一个新的操作。

  • 修改原始的类

    1. 添加一个新原子操作的最安全的方式是,修改原始的类,以支持期望的操作。
    2. 但是你可能无法访问源代码或者没有修改的自由,所以这通常是不可能的。
    3. 即使你可以修改原始的类,也需要理解其实现的同步策略,才能在维持原有设计的前提下完善它的功能。
    4. 直接向类中加入新方法,意味着所有实现类同步策略的代码仍然包含在一个源代码文件中,因此便于理解与维护。
  • 扩展这个类

    1. 另一种方法是扩展这个类,假如这个类在设计上是可以扩展的。如:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      import net.jcip.annotations.ThreadSafe;

      import java.util.Vector;

      @ThreadSafe
      public class BetterVector <E> extends Vector<E> {
      /** 扩展可序列化类时,应重新定义 serialVersionUID */
      static final long serialVersionUID = -3963416950630760754L;

      public synchronized boolean putIfAbsent(E x) {
      boolean absent = !contains(x);
      if (absent) {
      add(x);
      }
      return absent;
      }
      }
    2. 扩展后,同步策略的实现会被分布到多个独立维护的源代码(.class)文件中,所以扩展一个类比直接在类中加入代码更脆弱。

    3. 如果低层的类选择了不同的锁保护它的状态变量,从而会改变它的同步策略,子类就在不知不觉中被破坏,因为它不能再用正确的锁控制对基类状态的并发访问。

4.2 客户端加锁

创建一个助手类,该助手类包含一个作用于线程安全 List 的原子 缺少即加入 的操作。

  1. 非线程安全的 “缺少即加入” 实现(不要这样做)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    @NotThreadSafe
    class BadListHelper <E> {
    public List<E> list = Collections.synchronizedList(new ArrayList<E>());

    public synchronized boolean putIfAbsent(E x) {
    boolean absent = !list.contains(x);
    if (absent) {
    list.add(x);
    }
    return absent;
    }

    // =========================
    // 等等有关操作 list 的其它方法
    // =========================
    }

    这里是并不能保证对 list 的操作是线程安全的,即使使用了同步修饰 putIfAbsent() 方法。

    虽然对 putIfAbsent() 加了内置锁,但是这仅仅限制同一时间仅有单一线程调用此方法。

    我们并不能保证其它线程调用 BadListHelper 的其它方法,对 list 进行操作。即使其它方法也都是用了内置锁。

  2. 使用客户端加锁实现的 “缺少即加入”

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    @ThreadSafe
    class GoodListHelper <E> {
    public List<E> list = Collections.synchronizedList(new ArrayList<E>());

    public boolean putIfAbsent(E x) {
    synchronized (list) {
    boolean absent = !list.contains(x);
    if (absent) {
    list.add(x);
    }
    return absent;
    }
    }

    // =========================
    // 等等有关操作 list 的其它方法
    // =========================
    }

    线程安全

  • 如果说,为了添加另一个原子操作而去扩展一个类(扩展类加锁)容易出问题,是因为它将加锁的代码分布到了继承体系中的多个类里。
  • 然而客户端加锁其实是更加脆弱的,因为它必须将类 a.class中的加锁代码置入与a.class完全无关的类中。
  • 在那些不关注锁策略的类中使用客户端加锁时,一定要小心。客户端加锁与扩展类有很多共同之处:所得类的行为与基类的实现之间都存在耦合。正如同扩展会破坏实现的封装性一样,客户端加锁会破坏同步策略的封装性。

4.3 组合(composition)

向己有的类中添加一个原子操作,还有更好的选择:组合。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
import net.jcip.annotations.ThreadSafe;

import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

/**
* Created by osys on 2022/08/28 21:48.
*/
@ThreadSafe
public class ImprovedList<T> implements List<T> {
private final List<T> list;

/**
* PRE: list 参数是线程安全的。
*/
public ImprovedList(List<T> list) {this.list = list;}

public synchronized boolean putIfAbsent(T x) {
boolean contains = list.contains(x);
if (contains) {
list.add(x);
}
return !contains;
}

// List 方法的普通委托。
// 可变方法必须同步以确保 putIfAbsent 的原子性。
@Override
public synchronized void clear() {list.clear();}
@Override
public synchronized void add(int index, T element) {list.add(index, element);}
@Override
public synchronized boolean add(T e) {return list.add(e);}
@Override
public synchronized boolean remove(Object o) {return list.remove(o);}
@Override
public synchronized boolean addAll(Collection<? extends T> c) {return list.addAll(c);}
@Override
public synchronized boolean addAll(int index, Collection<? extends T> c) {return list.addAll(index, c);}
@Override
public synchronized boolean removeAll(Collection<?> c) {return list.removeAll(c);}
@Override
public synchronized boolean retainAll(Collection<?> c) {return list.retainAll(c);}
@Override
public synchronized T set(int index, T element) {return list.set(index, element);}
@Override
public synchronized T remove(int index) {return list.remove(index);}

// 不可变方法
@Override
public T get(int index) {return list.get(index);}
@Override
public int size() {return list.size();}
@Override
public boolean isEmpty() {return list.isEmpty();}
@Override
public boolean contains(Object o) {return list.contains(o);}
@Override
public Iterator<T> iterator() {return list.iterator();}
@Override
public Object[] toArray() {return list.toArray();}
@Override
public <T> T[] toArray(T[] a) {return list.toArray(a);}
@Override
public boolean containsAll(Collection<?> c) {return list.containsAll(c);}
@Override
public boolean equals(Object o) {return list.equals(o);}
@Override
public int hashCode() {return list.hashCode();}
@Override
public int indexOf(Object o) {return list.indexOf(o);}
@Override
public int lastIndexOf(Object o) {return list.lastIndexOf(o);}
@Override
public ListIterator<T> listIterator() {return list.listIterator();}
@Override
public ListIterator<T> listIterator(int index) {return list.listIterator(index);}
@Override
public List<T> subList(int fromIndex, int toIndex) {return list.subList(fromIndex, toIndex);}
}
  • ImprovedList 通过将操作委托给底层的 List 实例,实现了 List 的操作,同时还添加了一个原子的putIfAbsent()方法。
  • 就像 Collections.synchronizedList 和其他容器封装器那样,ImprovedList 假设一旦有一个 list 传给它的构造函数后,客户将不再直接使用这个 list,而仅仅通过 ImprovedList 访问它。
  • 通过使用内部锁,ImproveaList 引入了一个新的锁层
  • 它并不关心底层的 List 是否线程安全,即使 List 不是线程安全的,或者会改变 ImproveaList 的锁实现,Improvedtist 都有自己兼容的锁可以提供线程安全性。
  • 虽然额外的一层同步可能会带来一些微弱的性能损失,但是相比于去尝试模拟另一个对象的锁策路而言,ImprovedList并不那么脆弱。
  • 我们己经使用 Java 监视器模式有效地封裝了一个己有的 List,而且只要我们的类持有底层 List 的唯一外部引用,那么就能保证提供线程安全性。

5. 同步策略的文档化

1.为类的用户编写类线程安全性担保的文档;为类的维护者编写类的同步文档。

2.每次使用synchronized,volatile或者任何线程安全类,都表现了一种同步策略,这个策略是你程序设计的一个元素,因此应该将它文档化。

Java并发编程实战:第4章 组合对象

https://osys.github.io/posts/f8f2.html

作者

Osys

发布于

2022年08月29日

更新于

2022年08月29日

许可协议

评论