当一个对象被封装到另一个对象中,能够访问被封装对象的所有代码路径都是已知的。
通过封闭和加锁,可以确保以线程安全的方式使用非线程安全的对象
class MutablePoint {
public int x, y;
public MutablePoint() {
x = 0;
y = 0;
}
public MutablePoint(MutablePoint p) {
this.x = p.x;
this.y = p.y;
}
}
将MutablePoint封闭到MonitorVehicleTracker,返回对象通过拷贝生成新的对象,并对get和set加锁
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);
}
}
但数据量大时,每次拷贝会降低性能
将域声明为final变成不可变对象
class Point {
public final int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
将线程安全委托给ConcurrentMap,返回实时更新且只读的对象引用
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);
}
}
若需要非实时更新且只读的对象引用,因为map内容是不可变的,可以浅拷贝
public Map getLocations() {
return Collections.unmodifiableMap(new HashMap<>(locations));
}
若想要可变且线程安全的Point类,需要注意get应该同时获得x和y,若分别提供的话,可能会在两次调用中导致状态不一致
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.x = x;
this.y = y;
}
public synchronized int[] get() {
return new int[]{x, y};
}
public synchronized void set(int x, int y) {
this.x = x;
this.y = y;
}
}
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);
}
}
最简单的方法是修改原始类,若没有源代码通过扩展实现,如下实现putIfAbsent()
class BetterVector extends Vector {
public synchronized boolean putIfAbsent(E x) {
boolean absent = !contains(x);
if (absent)
add(x);
return absent;
}
}
但如果底层的类改变了同步策略并选择了不同的锁来保护它的状态变量,那么子类会被破坏
客户端加锁是指,对于使用某个对象X的客户端代码,使用X本身用于保护其状态的锁来保护这段客户代码
对于由Collections.synchronizedList封装的ArrayList等类,可通过辅助类
class ListHelper {
public List list = Collections.synchronizedList(new ArrayList<>());
public boolean putIfAbsent(E x) {
boolean absent = !list.contains(x);
if (absent)
list.add(x);
return absent;
}
}
但上面的做法是错误的,因为synchronizedList内部的锁和ListHelper的锁不是同一个
class ListHelper {
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;
}
}
}
但会将类的加锁代码放到与其完全无关的其他类中,会破坏同步策略的封装性
将操作委托给底层的list,并新增加锁方法
class ImprovedList implements List {
private final List list;
public ImprovedList(List list) {
this.list = list;
}
public synchronized boolean putIfAbsent(T x) {
boolean absent = !list.contains(x);
if (absent)
list.add(x);
return absent;
}
//......
}
通过自身的内置锁增加了一层额外的加锁,即使List不是线程安全的或者修改了它的加锁实现,ImprovedList仍是线程安全的