尽管所有的状态都存储在公共静态变量(域)中,仍然能写出线程安全的程序,但比起那些适当封装的类来说,我们难以验证这种程序的线程安全性,也很难再修改它的同步不破坏它的线程安全性。在没有进行全局检查的情况下,封装能够保证线程的安全性。
=====设计线程安全类的过程应该包括下面3个基本要素:========
1、确定对象状态是由哪些变量组成的;
2、确定限制状态变量的不变约束;
3、制定一个管理并发访问对象状态的策略。
对状态使用同步策略,以维护其不变约束。需要明确的是同步的需求是什么,并分析其不可变约束和后验条件。
package net.jcip.examples;
import net.jcip.annotations.*;
/**
* Counter
*
* Simple thread-safe counter using the Java monitor pattern
*
* @author Brian Goetz and Tim Peierls
*/
@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;
}
}
注:不理解对象的不可变约束和后验条件,就不能保证线程的安全性。要约束状态变量的有效值或者状态转换,就需要原子性与封装性。
限制性使构造线程安全的类更容易。因为类的状态被限制后,分析它的线程安全性时,就不必检查完整的程序。
将数据封装在实体内部,把对数据的访问限制在对象的方法上,更易确保线程在访问数据时总能获得正确的锁。示例如下:
package net.jcip.examples;
import java.util.*;
import net.jcip.annotations.*;
/**
* PersonSet
*
* Using confinement to ensure thread safety
*
* @author Brian Goetz and Tim Peierls
*/
@ThreadSafe
public class PersonSet {
@GuardedBy("this") private final Set mySet = new HashSet();
public synchronized void addPerson(Person p) {
mySet.add(p);
}
public synchronized boolean containsPerson(Person p) {
return mySet.contains(p);
}
interface Person {
}
}
HashSet是一个线程安全的类,通过公开的方法进行同步读写操作,确保了线程的安全性。
像Vector和HashTable这样的核心库类,都是采用了Java监视器模式,其最大优势在于简单。Java监视器模式仅仅是一种习惯约定:任意锁对象只要始终如一地使用,都可以用来保护对象的状态。来看个私有锁保护状态的示例:
package net.jcip.examples;
import net.jcip.annotations.*;
/**
* PrivateLock
*
* Guarding state with a private lock
*
* @author Brian Goetz and Tim Peierls
*/
public class PrivateLock {
private final Object myLock = new Object();
@GuardedBy("myLock") Widget widget;
void someMethod() {
synchronized (myLock) {
// Access or modify the state of widget
}
}
}
实际上同步策略中Counter.java也是一个简单的Java监视器模式的示例。下面是一个基于监视器的机动车追踪器:
package net.jcip.examples;
import java.util.*;
import net.jcip.annotations.*;
/**
* MonitorVehicleTracker
*
* Monitor-based vehicle tracker implementation
*
* @author Brian Goetz and Tim Peierls
*/
@ThreadSafe
public class MonitorVehicleTracker {
@GuardedBy("this") private final Map locations;
public MonitorVehicleTracker(Map locations) {
this.locations = deepCopy(locations);
}
public synchronized Map 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;
}
private static Map deepCopy(Map m) {
Map result = new HashMap();
for (String id : m.keySet())
result.put(id, new MutablePoint(m.get(id)));
return Collections.unmodifiableMap(result);
}
}
package net.jcip.examples;
import net.jcip.annotations.*;
/**
* MutablePoint
*
* Mutable Point class similar to java.awt.Point
*
* @author Brian Goetz and Tim Peierls
*/
@NotThreadSafe
public class MutablePoint {
public int x, y;
public MutablePoint() {
x = 0;
y = 0;
}
public MutablePoint(MutablePoint p) {
this.x = p.x;
this.y = p.y;
}
}
委托线程安全,就是使用现有的线程安全的类来实现线程安全,即由其代为控制线程安全。下面来看个使用委托的机动车追踪器,ConcurrentMap线程安全委托:
package net.jcip.examples;
import java.util.*;
import java.util.concurrent.*;
import java.awt.*;
import java.awt.Point;
import net.jcip.annotations.*;
/**
* DelegatingVehicleTracker
*
* Delegating thread safety to a ConcurrentHashMap
*
* @author Brian Goetz and Tim Peierls
*/
@ThreadSafe
public class DelegatingVehicleTracker {
private final ConcurrentMap locations;
private final Map unmodifiableMap;
public DelegatingVehicleTracker(Map points) {
locations = new ConcurrentHashMap(points);
unmodifiableMap = Collections.unmodifiableMap(locations);
}
public Map getLocations() {
return unmodifiableMap;
}
public Point getLocation(String id) {
return locations.get(id);
}
public void setLocation(String id, int x, int y) {
if (locations.replace(id, new Point(x, y)) == null)
throw new IllegalArgumentException("invalid vehicle name: " + id);
}
// Alternate version of getLocations (Listing 4.8)
public Map getLocationsAsStatic() {
return Collections.unmodifiableMap(
new HashMap(locations));
}
}
package net.jcip.examples;
import net.jcip.annotations.*;
/**
* Point
*
* Immutable Point class used by DelegatingVehicleTracker
*
* @author Brian Goetz and Tim Peierls
*/
@Immutable
public class Point {
public final int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
Point是不可变对象,它是线程安全的。来看一下将线程安全委托到多个隐含变量:
package net.jcip.examples;
import java.awt.event.KeyListener;
import java.awt.event.MouseListener;
import java.util.*;
import java.util.concurrent.*;
/**
* VisualComponent
*
* Delegating thread safety to multiple underlying state variables
*
* @author Brian Goetz and Tim Peierls
*/
public class VisualComponent {
private final List keyListeners = new CopyOnWriteArrayList();
private final List 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);
}
}
注:如果一个类由多个彼此独立的线程安全的状态变量组成,并且类的操作不包含任何无效状态转换时,可以将线程安全委托给这些变量。如果一个状态变量是线程安全的,没有任何不变约束限制它的值,并且没有任何状态转换限制它的操作,那么它可以被安全地发布。
底层可变的状态也可以是线程安全的类。示例:
package net.jcip.examples;
import net.jcip.annotations.*;
/**
* SafePoint
*
* @author Brian Goetz and Tim Peierls
*/
@ThreadSafe
public class SafePoint {
@GuardedBy("this") private int x, y;
private SafePoint(int[] a) {
this(a[0], a[1]);
}
public SafePoint(SafePoint p) {
this(p.get());
}
public SafePoint(int x, int y) {
this.set(x, y);
}
public synchronized int[] get() {
return new int[]{x, y};
}
public synchronized void set(int x, int y) {
this.x = x;
this.y = y;
}
}
package net.jcip.examples;
import java.util.*;
import java.util.concurrent.*;
import net.jcip.annotations.*;
/**
* PublishingVehicleTracker
*
* Vehicle tracker that safely publishes underlying state
*
* @author Brian Goetz and Tim Peierls
*/
@ThreadSafe
public class PublishingVehicleTracker {
private final Map locations;
private final Map unmodifiableMap;
public PublishingVehicleTracker(Map locations) {
this.locations = new ConcurrentHashMap(locations);
this.unmodifiableMap = Collections.unmodifiableMap(this.locations);
}
public Map 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);
}
}
重要思想:缺少即加入。
扩展Vector为例:
package net.jcip.examples;
import java.util.*;
import net.jcip.annotations.*;
/**
* BetterVector
*
* Extending Vector to have a put-if-absent method
*
* @author Brian Goetz and Tim Peierls
*/
@ThreadSafe
public class BetterVector extends Vector {
// When extending a serializable class, you should redefine serialVersionUID
static final long serialVersionUID = -3963416950630760754L;
public synchronized boolean putIfAbsent(E x) {
boolean absent = !contains(x);
if (absent)
add(x);
return absent;
}
}
使用客户端加锁(缺少即加入):明确可变约束,加锁的位置很重要。
package net.jcip.examples;
import java.util.*;
import net.jcip.annotations.*;
/**
* ListHelder
*
* Examples of thread-safe and non-thread-safe implementations of
* put-if-absent helper methods for List
*
* @author Brian Goetz and Tim Peierls
*/
@NotThreadSafe
class BadListHelper {
public List list = Collections.synchronizedList(new ArrayList());
public synchronized boolean putIfAbsent(E x) {
boolean absent = !list.contains(x);
if (absent)
list.add(x);
return absent;
}
}
@ThreadSafe
class GoodListHelper {
public List list = Collections.synchronizedList(new ArrayList());
public boolean putIfAbsent(E x) {
synchronized (list) {
boolean absent = !list.contains(x);
if (absent)
list.add(x);
return absent;
}
}
}
使用组合实现:
package net.jcip.examples;
import java.util.*;
import net.jcip.annotations.*;
/**
* ImprovedList
*
* Implementing put-if-absent using composition
*
* @author Brian Goetz and Tim Peierls
*/
@ThreadSafe
public class ImprovedList implements List {
private final List list;
/**
* PRE: list argument is thread-safe.
*/
public ImprovedList(List list) { this.list = list; }
public synchronized boolean putIfAbsent(T x) {
boolean contains = list.contains(x);
if (contains)
list.add(x);
return !contains;
}
public synchronized void clear() { list.clear(); }
// ..................
}