在这种情况下,是否会抛出此AssertionError? [英] Will this AssertionError never be thrown in this case?

查看:354
本文介绍了在这种情况下,是否会抛出此AssertionError?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

首先关闭代码,从JCIP列出 http://jcip.net/listings/StuffIntoPublic.java
http://jcip.net/listings/Holder.java

  public class SafePublication {
public static void main(String [] args)throws InterruptedException {
// System.out.println(Thread.currentThread()。getName());
StuffIntoPublic t = new StuffIntoPublic();
t.initialize();
while(true){
new Thread(() - > {t.holder.assertSanity();})。start();
}
}
}

// @作者Brian Goetz和Tim Peierls
class StuffIntoPublic {
公众持有人;

public void initialize(){
// System.out.println(Thread.currentThread()。getName());
持有人=新持有人(42);
}
}

// @作者Brian Goetz和Tim Peierls
类持有人{
private int n;

public Holder(int n){
this.n = n;
}

public void assertSanity(){
if(n!= n){
throw new AssertionError(This statement is false。);
}
}
}

我说的是AssertionError因为Thread.start()在保证之前发生,所以在这种情况下永远不会抛出。注释的两个System.out.printlns都打印main,这意味着主线程通过在while(true)循环中的线程上创建和调用start来生成所有后来的线程。



由于那是创建和初始化Holder的线程,所有后续线程都可以安全地成为一个完全可见的持有者,因为之前发生了保证。我是对的吗?



我甚至尝试运行此代码很长时间没有断言错误。



但是,如果main看起来如下,那么我相信AssertionError可能是

  public static void main(String [ ] args)抛出InterruptedException {
System.out.println(Thread.currentThread()。getName());
StuffIntoPublic t = new StuffIntoPublic();
new Thread(() - > t.initialize())。start();
while(true){
new Thread(() - > {t.holder.assertSanity();})。start();
}
}


解决方案

是,这是安全的,因为线程#start 保证发生在之前。更加罗嗦:对 Thread #start 之前发生的任何变量的任何读/写(如果你想要,我倾向于按程序顺序认为 ),也将发生之前该线程内的任何操作(它的运行方法)。



<事实上,如果之前没有发生(允许重新排序)并且程序执行允许那些潜在的重新排序,那么可能会发生破坏并抛出该错误。我甚至倾向于说一个适用于弱内存模型的CPU(假设你是英特尔,这是一个强大的内存模型)可以增加这个机会,但我不确定。



因此,据我所知,这些操作将按以下顺序进行:首先,使用变量 n重新排序发布引用(之前没有发生过,所以这是允许的)。 Thread1创建 Holder 的实例。 Thread2看到发布的引用并调用该方法。它读取变量 n (请记住重新排序发生并且 n 尚未编写,因此默认值为),因此它会执行!= 检查,但是创建 Holder 的Thread1,将 n 写为 12 例如之前 Thread2再次读取它(在!= n 部分中)。所以这可能会失败。



使值 final 可以解决这个问题,因为它会引入正确的内存障碍,或者发生在之前规则。


First off the code, from JCIP listing http://jcip.net/listings/StuffIntoPublic.java and http://jcip.net/listings/Holder.java

public class SafePublication {
    public static void main(String[] args) throws InterruptedException {
//        System.out.println(Thread.currentThread().getName());
        StuffIntoPublic t = new StuffIntoPublic();
        t.initialize();
        while (true) {
            new Thread(() -> { t.holder.assertSanity(); }).start();
        }
    }
}

//@author Brian Goetz and Tim Peierls
class StuffIntoPublic {
    public Holder holder;

    public void initialize() {
//        System.out.println(Thread.currentThread().getName());
        holder = new Holder(42);
    }
}

//@author Brian Goetz and Tim Peierls
class Holder {
    private int n;

    public Holder(int n ) {
        this.n = n;
    }

    public void assertSanity() {
        if (n != n) {
            throw new AssertionError("This statement is false.");
        }
    }
}

I am saying that the AssertionError will never be thrown in this case because of the Thread.start() happens before guarantee. Both of the System.out.printlns that are commented prints main, meaning that the main thread is which spawns all the later threads by making and calling start on the threads in the while(true) loop.

And since that is the thread that created and initialized Holder, all subsequent threads are safe to be a perfectly visible holder due to the happens-before guarantee. Am I right?

I even tried running this code for a really long time and no assertion errors.

However, if the main looked like below, then I believe it will be possible for an AssertionError

 public static void main(String[] args) throws InterruptedException {
        System.out.println(Thread.currentThread().getName());
        StuffIntoPublic t = new StuffIntoPublic();
        new Thread(() -> t.initialize() ).start();
        while (true) {
            new Thread(() -> { t.holder.assertSanity(); }).start();
        }
    }

解决方案

Yes, this is safe, because Thread#start guarantees happens-before. To be more wordy: any read/write to any variable that happens before Thread#start (I tend to think above in program order if you want), will also happen before any action within that Thread (it's run method).

Indeed, that could happen to break and throw that error, if there were no happens before (to allow re-orderings) and if program execution would allow those potential re-orderings. I am even inclined to say and a proper CPU used with weak memory model (assuming you are on Intel, which is a strong memory model) could increase that chance, but I am not sure.

So, as far as I can tell, the actions would take place in the following order: first the the publishing the reference is re-ordered with variable n (there is no happens-before, so this is allowed). Thread1 creates an instance of Holder. Thread2 sees that published reference and calls that method. It reads the variable n to be zero (remember that re-ordering happened and n is not yet written, thus has a default value of zero), so it then does the != check, but Thread1 that created Holder, writes n to be 12 for example before Thread2 reads it again (in the !=n part). So this can fail.

Making the value final would solve this as it introduces the right memory barriers, or happens-before rules.

这篇关于在这种情况下,是否会抛出此AssertionError?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆