java并发编程实战:第四章----对象的组合

一、设计线程安全的类

  • 找出构造对象状态的所有变量(若变量为引用类型,还包括引用对象中的域)
  • 约束状态变量的不变性条件
  • 建立对象状态的并发访问管理策略(规定了如何维护线程安全性)

1、收集同步需求(找出复合操作、多个变量遵循原子性的操作等)

2、依赖状态的操作(找出操作是否基于先验条件,例:取出当队列不为空)

3、状态的所有权(对象被哪些线程所有,哪些线程可以操作对象)

二、实例封闭

将数据封装在对象内部,可以将数据的访问限制在对象的方法上,确保简单正确的持有锁。(因为无需观察整个程序,只需检查当前类)

Java提供包装器对ArrayList、HashMap等容器对象提供线程安全保护

1、Java监视器模式:把对象的所有可变状态封装起来,并使用对象的内置锁保护

使用私有锁而不是内置锁的优点:(private final Object myLock = new Object();) 

  • 私有锁可以将锁封装起来,客户代码无法得到锁
  • 客户可以通过公有方法来访问锁,以便参与到同步策略中去。

2、实例:基于监视器模式的车辆追踪

MutablePoint不一定是线程安全的,但该类一定是线程安全的,因为它包含的Map对象与Map中的Point对象都未曾发布,并且满足实例封闭。

三、线程安全性的委托

通过多个线程安全的类组成的类不一定是线程安全的

委托:通过委托类的线程安全性判断被委托类的线程安全性

1、委托给单个线程安全状态变量可以保证线程安全性

2、委托给多个相互独立的线程安全状态变量可以保证线程安全性

3、如果类包含多个线程安全状态变量的符合操作,则无法保证线程安全性,可以通过加锁机制保证

4、发布:如果一个状态变量是线程安全的,并且没有不变性条件约束它(例:大于0),在变量操作上没有不允许的状态转换,则可以安全发布

5、安全发布底层状态的线程安全类

 1 @ThreadSafe
 2 public class PublishingVehicleTracker {
 3     private final Map<String, SafePoint> locations;
 4     private final Map<String, SafePoint> unmodifiableMap;
 5 
 6     public PublishingVehicleTracker(Map<String, SafePoint> locations) {
 7         this.locations = new ConcurrentHashMap<String, SafePoint>(locations);
 8         this.unmodifiableMap = Collections.unmodifiableMap(this.locations);
 9     }
10 
11     public Map<String, SafePoint> getLocations() {
12         return unmodifiableMap;
13     }
14 
15     public SafePoint getLocation(String id) {
16         return locations.get(id);
17     }
18 
19     public void setLocation(String id, int x, int y) {
20         if (!locations.containsKey(id))
21             throw new IllegalArgumentException("invalid vehicle name: " + id);
22         locations.get(id).set(x, y);
23     }
24 }

四、现有的线程安全类中添加功能

例:在Vector中添加”若没有则添加“功能,可以使用拓展的办法,BetterVector继承至Vector并对该符合操作通过同步机制增加原子性

1、客户端加锁机制

  使用某个对象的代码时必须使用该对象本身用于保护其状态的锁,不推荐(同步的实现被分到两个不相关的类中)

当没有使用同一个锁,不足以提供线程安全保护:

2、组合

ImprovedList将List的操作委托给底层的list实例来操作,并通过自身的内置锁增加一层额外的加锁,同时添加了新的同步方法。

五、将同步策略文档化

原文地址:https://www.cnblogs.com/linghu-java/p/9025190.html