这是原始代码
//@author Brian Goetz and Tim Peierls
@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.set(x, y);
}
public synchronized int[] get() {
return new int[]{x, y};
}
public synchronized void set(int x, int y) {
this.x = x;
this.y = y;
}
}
在这里,私有(private)int x,y不是最终值是很好的,因为构造函数中的set方法会在调用get时在关系发生之前发生,因为它们使用相同的锁。
现在,这里是修改后的版本和主要方法,我希望在运行该方法后再抛出AssertionError,因为我在set方法中删除了synced关键字。如果有人指出它不是线程安全的,我就将构造函数私有(private)化为唯一的调用者,这不是我关注的重点。
无论如何,我已经等了很长时间,并且没有抛出AssertionErrors。现在,我厌倦了这种修改过的类在某种程度上是线程安全的,即使从我学到的知识来看,这也不是因为x和y不是最终的。有人可以告诉我为什么仍然从不抛出AssertionError吗?
public class SafePointProblem {
static SafePoint sp = new SafePoint(1, 1);
public static void main(String[] args) {
new Thread(() -> {
while (true) {
final int finalI = new Random().nextInt(50);
new Thread(() -> {
sp = new SafePoint(finalI, finalI);
}).start();
}
}).start();
while (true) {
new Thread(() -> {
sp.assertSanity();
int[] xy = sp.get();
if (xy[0] != xy[1]) {
throw new AssertionError("This statement is false 1.");
}
}).start();
}
}
}
class SafePoint {
private int x, y;
public SafePoint(int x, int y) {
this.set(x, y);
}
public synchronized int[] get() {
return new int[]{x, y};
}
// I removed the synchronized from here
private void set(int x, int y) {
this.x = x;
this.y = y;
}
public void assertSanity() {
if (x != y) {
throw new AssertionError("This statement is false2.");
}
}
}
最佳答案
您已经运行了很多时间这一事实并不意味着任何事情,仅意味着您目前尚未复制此代码。可能使用不同的jre
或CPU
,这可能会中断。尤其糟糕的是,由于墨菲法则将保证这种情况将在生产中的某处发生,并且您将面临调试的噩梦。
一个小例子并不能证明代码是否正确/正确,对于并发代码尤其如此-这非常困难(我什至不敢说我完全理解它)。而且您确实知道,这很可能是不好的,因为之前没有发生任何事情。
另外,将这些变量设置为final
意味着您不能通过setters
设置它们,而只能在构造函数中设置。因此,这意味着您将没有 setter ,因此,一旦设置了x
和y
字段,就没有人可以更改它们,因此get
根本不应该同步(我在这里谈论的是SafePoint
)