这是原始代码
//@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; } }
这里私有的int x,y不是最终值是很好的,因为构造函数中的set方法会在调用get时在关系发生之前发生,因为它们使用相同的锁。
现在,这里是修改后的版本和主要方法,由于在set方法中删除了synced关键字,因此我希望在运行它后再抛出AssertionError。如果有人指出它不是线程安全的,那我就将构造函数私有化为唯一的调用者,这不是我关注的重点。
无论如何,我现在已经等了很多时间,并且没有抛出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可能会中断。尤其糟糕的是,由于墨菲法则将保证这种情况将在生产环境中发生,并且您将面临调试的噩梦。
jre
CPU
一个小例子并不能证明代码是否正确/正确,对于并发代码尤其如此-这非常困难(我什至不敢说我完全理解它)。而且您确实知道,这很可能是不好的,因为之前没有发生任何事情。
同时设置这些变量final将意味着您不能通过设置它们setters,而只能在构造函数中进行设置。因此,这意味着您将没有设置器,因此没有人可以更改字段,x并且y一旦设置了它们,get就根本不应被同步(我在SafePoint这里谈论您的是)
final
setters
SafePoint