Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 为什么即使运行了这么长时间,它也不会抛出断言错误?_Java_Multithreading_Concurrency_Visibility_Jit - Fatal编程技术网

Java 为什么即使运行了这么长时间,它也不会抛出断言错误?

Java 为什么即使运行了这么长时间,它也不会抛出断言错误?,java,multithreading,concurrency,visibility,jit,Java,Multithreading,Concurrency,Visibility,Jit,这是原始代码 //@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());

这是原始代码

//@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不是final是很好的,因为构造函数中的set方法在调用get时会产生一个before-before关系,因为它们使用相同的锁

现在这里是修改后的版本和一个主方法,我希望在运行它一段时间后抛出一个AssertionError,因为我删除了set方法中的synchronized关键字。我将它设置为私有,以便构造函数成为唯一调用它的人,以防有人指出它因为它而不是线程安全的,这不是我问题的重点

无论如何,我已经等了很久了,没有抛出断言错误。现在我厌倦了这个修改过的类在某种程度上是线程安全的,尽管从我所学到的,这并不是因为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.");
        }
    }
}

我不确定这个问题是否可以用简单的语言来回答,所以你很可能会得到某种未定义的行为

为了更深入地研究这个问题,我们可以尝试对其进行反编译。我用HotSpot C2编译器编译了这段代码。下面是我能找到的片段(整个编译代码太长):

0x00007f6b38516fbd:锁定地址$0x0,(%rsp)*同步条目
; - java.util.Random::@-1(第105行)
; - com.test.SafePointProblem$lambda::run@4(第19行)
0x00007f6b38516fc2:mov 0x10(%r10),%rax*invokevirtual compareAndSwapLong
; - java.util.concurrent.AtomicLong::compareAndSet@9(第147行)
; - java.util.Random::next@32(第204行)
; - java.util.Random::nextInt@17(第390行)
; - com.test.SafePointProblem$lambda
我不是HotSpot JIT编译器专家,但从我所看到的情况来看,编译后的代码在您的所有可运行程序中都包含同步。其中一些来自于原子和重置CPU存储缓冲区的
Random::next
(它使用CAS)


“为什么?”这个问题的详尽答案可能相当复杂,而且肯定依赖于平台。

您已经运行了很多时间,这一事实并不意味着什么,它只是意味着目前您没有复制它;可能使用不同的
jre
CPU
这可能会中断。尤其糟糕,因为墨菲定律将保证这将发生在生产中的某个地方,您将有一个噩梦要调试

一个小的例子并不能证明代码是好的/正确的,特别是对于并发代码,这是非常困难的(我甚至不敢说我完全理解它)。你知道这可能是不好的,因为以前没有发生过


另外,将这些变量设置为final将意味着您不能通过setters设置它们,而只能在构造函数中设置它们。这意味着你不能有一个setter,因此没有人可以在设置后改变字段
x
y
,因此
get
根本不应该是同步的(我在这里谈论的是你的
SafePoint

实际上我试图调查是否有可能抛出
AssertionError
。可能HotSpot服务器编译器进行了优化,但仍然不允许抛出
错误。我所发现的只是来自
随机
的同步点和启动线程。。。不太有用…也是投票,是的,我知道最后一个字段及其限制。听到在不同的jre或cpu上这种情况是如何发生的,这很有趣,感谢您确认对这种情况的关注before@katiex7np,但我真的无法理解被接受的答案(我不在乎你接受哪一个)是如何解释事情的。
lock addl
synchronization entry
触发了吗?啊,我以为random.nextint就是我之前所做的telp@katiex7别误会,但是
C1
编译器将生成的任何代码在这种情况下都是无关紧要的,唯一需要依赖的是规范——正如您已经得出相同的结论,您的代码在JLS规则下是不正确的,IMO是这里唯一重要的东西。这是一个非常有趣的答案。我可能需要做一个forloop,增加一个计数器,而不是使用random.nextint,然后看看会发生什么Upvoted@St.Antario我真的不知道这段代码实际上证明了什么
lock addl
是一个
StoreLoad
屏障。你在这里的意思是什么?@St.Antario还有一小部分代码不是规范的证明(或相反),因此即使某个编译器会做一些事情,其他编译器也可能不会。唯一需要遵守的是规范。@Eugene只是更仔细地查看了代码。必须同意,简单的负载围栏在这里不能证明一致性。此外,我看不到如何在此处获取
AssertionError
。@Eugene,因为读/写引用是原子的,新的
finalI=new Random()。如果没有使用
prefetchnta
指令,则在发布引用时(即使是不安全的),nextInt(50)
hb
new SafePoint(finalI,finalI)
。。。我看不出有什么办法。。。
  0x00007f6b38516fbd: lock addl $0x0,(%rsp)     ;*synchronization entry
                                                ; - java.util.Random::<init>@-1 (line 105)
                                                ; - com.test.SafePointProblem$lambda::run@4 (line 19)

  0x00007f6b38516fc2: mov     0x10(%r10),%rax   ;*invokevirtual compareAndSwapLong
                                                ; - java.util.concurrent.atomic.AtomicLong::compareAndSet@9 (line 147)
                                                ; - java.util.Random::next@32 (line 204)
                                                ; - java.util.Random::nextInt@17 (line 390)
                                                ; - com.test.SafePointProblem$lambda