实例封闭机制
当一个对象被封装到另一个对象中,能够访问被封装对象的所有代码路径都是已知的。
通过将封闭机制与合适的加锁策略结合起来,可以确保以线程安全的方式来使用非线程安全的对象。
将数据封装在对象内部,可以将数据的访问限制在对象的方法上,从而更容易确保线程在访问数据时总能持有正确的锁。
具体说明请看以下代码展示:
public class PersonSet {
private final Set<Person> myset = new HashSet<>();
public synchronized void addPerson(Person p) {
myset.add(p);
}
public synchronized boolean containsPerson(Person person) {
return myset.contains(person);
}
public static class Person {
}
}
Hashset 并非线程安全的,但由于 mySet 是私有的并且不会逸出,因此 HashSet 被封闭在 PersonSet 中。唯一能访问 mySet 的代码路径是 addPerson 和 containsPerson,在执行它们时都得先获取 PersonSet 上的锁。
如果 Person 类是可变的,那么在访问从 PersonSet 中获得的 Person 对象时,还需要额外的同步。
私有锁
遵循 Java 监视器模式的对象会把对象的所有可变状态都封装起来,并由对象自己的内置锁来保护。
public class PrivateLock {
private final Object myLock = new Object();
void someMethod() {
synchronized (myLock) {
// 访问或修改状态
}
}
}
使用私有的锁对象而不是对象的内置锁,有许多优点。
私有的锁对象可以将锁封装起来,使客户代码无法得到锁,但客户代码可以用过公有方法来访问锁,以便参与同步策略。
参考资料
- 《并发编程实战》