私有构造函数捕获模式
《Java并发编程实践》的注解中有提到这一概念。
The private constructor exists to avoid the race condition that would occur if the copy constructor were implemented as this (p.x, p.y); this is an example of the private constructor capture idiom (Bloch and Gafter, 2005).
结合原文代码:
@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.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;
}
}
这里的构造器public SafePoint(SafePoint p) { this (p.get()); }
是为了捕获另一个实例的状态。get()方法是一个同步方法,为了避免竞态没有分别提供x、y的公有getter方法。
为了保证SafePoint的多线程安全性,在使用另一个实例构造新的实例时,使用了一个私有的构造器。
首先为什么不用下面这种,还是为了避免竞态(p.x和p.y调用不是原子操作)。
public SafePoint(SafePoint p) {
this(p.x, p.y)
}
同理,这种也不行,两次调用get()方法不是原子操作。
public SafePoint(SafePoint p) {
this(p.get()[0], p.get()[1])
}
为什么不用直接用数组,编译不通过:Call to "this()" must be first statement in constructor body
public SafePoint(SafePoint p) {
int[] a = p.get();
this(a[0], a[1]);
}
为什么接受数组为参数的构造器不能公开,数组a是有外部传入的,并不能保证数组内容不会其他线程修改。
public SafePoint (int [] a) {
this (a[0], a[1]);
}
当然我们可以使用下面这种代替私有的构造器,这种方法是安全的,但是会产生重复的初始化代码。
public SafePoint(SafePoint p) {
int[] a = p.get();
this.x = a[0];
this.y = a[1];
}
再回头看SafePoint的线程安全性,SafePoint有两个状态变量x、y。为了保证线程安全性,没有为其分别提供getter和setter方法,而是将其封装后发布并使用内置锁保护。
可以参考stackoverflow上的示例代码。