gpt4 book ai didi

java - 避免竞争条件的私有(private)构造函数

转载 作者:IT老高 更新时间:2023-10-28 20:33:40 26 4
gpt4 key购买 nike

我正在阅读Java Concurrency in Practice session 4.3.5一书

  @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;
}

}

我不清楚它在哪里写的

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).

我知道它提供了一个 getter 来一次在数组中检索 x 和 y,而不是为每个单独的 getter,所以调用者会看到一致的值,但为什么是私有(private)构造函数?这里有什么诀窍

最佳答案

这里已经有很多答案,但我真的很想深入了解一些细节(就我的知识而言,让我吧)。我强烈建议您运行答案中出现的每个示例,以亲自了解事情的发生方式和原因。

要了解解决方案,首先要了解问题。

假设 SafePoint 类实际上如下所示:

class SafePoint {
private int x;
private int y;

public SafePoint(int x, int y){
this.x = x;
this.y = y;
}

public SafePoint(SafePoint safePoint){
this(safePoint.x, safePoint.y);
}

public synchronized int[] getXY(){
return new int[]{x,y};
}

public synchronized void setXY(int x, int y){
this.x = x;
//Simulate some resource intensive work that starts EXACTLY at this point, causing a small delay
try {
Thread.sleep(10 * 100);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.y = y;
}

public String toString(){
return Objects.toStringHelper(this.getClass()).add("X", x).add("Y", y).toString();
}
}

什么变量创建了这个对象的状态?只有两个:x,y。它们是否受到某种同步机制的保护?好吧,它们是通过内在锁,通过 synchronized 关键字 - 至少在 setter 和 getter 中。他们在其他任何地方都被“感动”了吗?当然在这里:

public SafePoint(SafePoint safePoint){
this(safePoint.x, safePoint.y);
}

你在这里所做的是读取你的对象。对于一个线程安全的类,你必须协调对它的读/写访问,或者在同一个锁上同步。但是这里没有发生这样的事情。 setXY 方法确实是同步的,但是克隆构造函数不是,因此调用这两个可以以非线程安全的方式完成。我们可以刹车吗?

让我们试试这个:

public class SafePointMain {
public static void main(String[] args) throws Exception {
final SafePoint originalSafePoint = new SafePoint(1,1);

//One Thread is trying to change this SafePoint
new Thread(new Runnable() {
@Override
public void run() {
originalSafePoint.setXY(2, 2);
System.out.println("Original : " + originalSafePoint.toString());
}
}).start();

//The other Thread is trying to create a copy. The copy, depending on the JVM, MUST be either (1,1) or (2,2)
//depending on which Thread starts first, but it can not be (1,2) or (2,1) for example.
new Thread(new Runnable() {
@Override
public void run() {
SafePoint copySafePoint = new SafePoint(originalSafePoint);
System.out.println("Copy : " + copySafePoint.toString());
}
}).start();
}
}

输出很容易就是这个:

 Copy : SafePoint{X=2, Y=1}
Original : SafePoint{X=2, Y=2}

这是逻辑,因为一个线程更新=写入我们的对象,另一个正在读取它。它们不会在某些公共(public)锁上同步,因此不会同步输出。

解决方案?

  • 同步构造函数,这样读取将在同一个锁上同步,但是Java中的构造函数不能使用同步关键字——这当然是逻辑。

  • 可能会使用不同的锁,比如可重入锁(如果同步关键字不能使用)。但它也不起作用,因为构造函数中的第一条语句必须是对 this/super 的调用。如果我们实现不同的锁,那么第一行必须是这样的:

    lock.lock()//这里的lock是ReentrantLock,编译器不会因为上面的原因允许这个。

  • 如果我们让构造函数成为一个方法呢?当然可以!

查看此代码示例

/*
* this is a refactored method, instead of a constructor
*/
public SafePoint cloneSafePoint(SafePoint originalSafePoint){
int [] xy = originalSafePoint.getXY();
return new SafePoint(xy[0], xy[1]);
}

调用看起来像这样:

 public void run() {
SafePoint copySafePoint = originalSafePoint.cloneSafePoint(originalSafePoint);
//SafePoint copySafePoint = new SafePoint(originalSafePoint);
System.out.println("Copy : " + copySafePoint.toString());
}

这一次代码按预期运行,因为读取和写入同步在同一个锁上,但是我们删除了构造函数。如果不允许这样做呢?

我们需要找到一种方法在同一个锁上同步读取和写入 SafePoint。

理想情况下,我们会想要这样的东西:

 public SafePoint(SafePoint safePoint){
int [] xy = safePoint.getXY();
this(xy[0], xy[1]);
}

但编译器不允许这样做。

我们可以通过调用 *getXY 方法来安全地读取,因此我们需要一种方法来使用它,但是我们没有一个构造函数来接受这样的参数 - 创建一个。

private SafePoint(int [] xy){
this(xy[0], xy[1]);
}

然后,实际的调用:

public  SafePoint (SafePoint safePoint){
this(safePoint.getXY());
}

注意构造函数是私有(private)的,这是因为我们不想暴露另一个公共(public)构造函数并再次考虑类的不变量,因此我们将其设为私有(private) - 只有我们可以调用它。

关于java - 避免竞争条件的私有(private)构造函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12028925/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com