卓二妹原创,转载请注明来源。
@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; } }
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).
这里如果将private SafePoint(int[] a) { this(a[0], a[1]); }方法改为public就会有race condition问题。为什么呢?
因为如果我们要复制一个SafePoint对象时,我们会首先想到编写下面这样一个方法:
public SafePoint(SafePoint safePoint){ this(safePoint.x, safePoint.y); }
但是当我们在通过复制originalSafePoint创建新的SafePoint对象时,可能有一个线程正在调用originalSafePoint.setXY方法,导致读取safePoint.x, safePoint.y这对值可能一个更新了而一个没更新,出现线程不安全。因此x,y必须同时读取出来。所以在复制的时候,不能像上面那样调用,而要用getXY()把x y一起取出来。
public SafePoint(SafePoint safePoint){ int [] xy = safePoint.getXY(); this(xy[0], xy[1]); }
上面的代码会有编译错误,因为this()必须是body的第一行:
那么好吧,那我们这样写就行了是吗?
public SafePoint(SafePoint originalSafePoint){ this(originalSafePoint.getXY()[0],originalSafePoint.getXY()[1]); }
显然这样也是有问题的,因为originalSafePoint.getXY()被调用了两次,这时候两次取出来的x y已经不是一对值了。
因此我们只有提供一个构造方法接受getXY()返回的数组作为参数,来复制一个对象:
public SafePoint(SafePoint originalSafePoint){ this(originalSafePoint.getXY()); } private SafePoint(int [] xy){ this(xy[0], xy[1]); }
这样就避免了上面的copy constructor的问题。现在再来读一下作者说的这句话就清楚很多了:
写道
The private constructor exists to avoid the race condition that would occur if the copy constructor were implemented as this(p.x, p.y);
私有的构造方法的存在,是为了避免在复制构造函数(即SafePoint(SafePoint originalSafePoint))的中因调用this(p.x, p.y)而引起的race condition。