当前位置:编程学习 > JAVA >>

并发编程中的安全发布问题

//不安全发布
public Holder holder;

public void initialize() {
    holder = new Holder(42);
}

//Holder的代码
public class Holder {
    private int n;

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

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


当在多线程环境下执行不安全代码发布的方式,这样会抛出AssertionError么?

里面的n!=n分别是什么含义?

跪求帮助。  --------------------编程问答-------------------- 小伙伴们!!!!快粗来!!!! --------------------编程问答-------------------- Holder 类有状态 但不提供setter方法 按理不会抛出AssertionError
再说n != n 自身不相等才为真 但n是基础类型 我认为不会抛异常
瞎猜的。。 --------------------编程问答-------------------- 求大神解脱。 --------------------编程问答-------------------- 没搞明白为什么会有不安全发布?在构造函数中发布该对象才可能产生不安全发布,构造函数中没有发布这个对象,怎么会产生不安全发布呢?  --------------------编程问答--------------------
引用 4 楼 pangjingji 的回复:
没搞明白为什么会有不安全发布?在构造函数中发布该对象才可能产生不安全发布,构造函数中没有发布这个对象,怎么会产生不安全发布呢? 

一个线程拿到可能是null的Holder引用,而一个可能拿到初始化好的,这样不是不安全么?
--------------------编程问答-------------------- 好多年没在CSDN回答问题了,出来活动活动筋骨

首先LZ要理解什么叫做“正确构造”
4楼所说的情况属于没有“正确构造”,也就是对象构造期间发生了“this逸出”
这种情况代码如下所示

public class Holder {
    private int n;
 
    public Holder() {
        this.n = n;
        outter = this; //这里this逸出(outter是一个外部使用的对象)
    }
 
    public void assertSanity() {
        if(n != n)
              throw new AssertionError("This statement is false.");
    }
}

没有正确构造的对象,无论如何不能“安全发布”

那么一个正确构造了的对象是否能“安全发布”呢?
答案是看情况
如果是“不可变对象”,只要正确构造了,无论如何JVM都能保证它被“安全发布”。
如果不是“不可变对象”,即使正确构造,但是JVM并不保证它能被“安全发布”。
差别在哪里呢?就是在多线程环境下,这种没有能“安全发布”情况代码如下所示(使用LZ正确构造的Holder):

public class SuperHolder
{
    public Holder holder;

    public static void main(String... args)
    {
          final SuperHolder sh = new SuperHolder();

          new Thread(new Runnable(){
              public void run()
              {
                   sh.holder = new Holder();
              }
          }).start();

          new Thread(new Runnable(){
              public void run()
              {
                   if(sh.holder!=null)
                   {
                        sh.holder.assertSanity();
                   }
              }
          }).start();
    }
}

这种情况,就可能出现LZ问的这个问题。不过很难观察到,特别在X86架构下。
为什么会出现这个问题,原因就是构造函数不是原子的,指令可能重排序。

LZ明白了吗? --------------------编程问答-------------------- ‘但是JVM并不保证它能被“安全发布”’,稍微修改一下:‘但是它的“安全发布”并不由JVM来保证,需要用户自己来保证它的“安全发布”’

恩...这样更贴切一些 --------------------编程问答--------------------
引用 6 楼 runshine 的回复:
好多年没在CSDN回答问题了,出来活动活动筋骨

首先LZ要理解什么叫做“正确构造”
4楼所说的情况属于没有“正确构造”,也就是对象构造期间发生了“this逸出”
这种情况代码如下所示

public class Holder {
    private int n;
 
    public Holder() {
        this.n = n;
        outter = this; //这里this逸出(outter是一个外部使用的对象)
    }
 
    public void assertSanity() {
        if(n != n)
              throw new AssertionError("This statement is false.");
    }
}

没有正确构造的对象,无论如何不能“安全发布”

那么一个正确构造了的对象是否能“安全发布”呢?
答案是看情况
如果是“不可变对象”,只要正确构造了,无论如何JVM都能保证它被“安全发布”。
如果不是“不可变对象”,即使正确构造,但是JVM并不保证它能被“安全发布”。
差别在哪里呢?就是在多线程环境下,这种没有能“安全发布”情况代码如下所示(使用LZ正确构造的Holder):

public class SuperHolder
{
    public Holder holder;

    public static void main(String... args)
    {
          final SuperHolder sh = new SuperHolder();

          new Thread(new Runnable(){
              public void run()
              {
                   sh.holder = new Holder();
              }
          }).start();

          new Thread(new Runnable(){
              public void run()
              {
                   if(sh.holder!=null)
                   {
                        sh.holder.assertSanity();
                   }
              }
          }).start();
    }
}

这种情况,就可能出现LZ问的这个问题。不过很难观察到,特别在X86架构下。
为什么会出现这个问题,原因就是构造函数不是原子的,指令可能重排序。

LZ明白了吗?

谢谢啊!这么久的第一次给了我。深感荣幸!
不过,还是不很懂啊。你说的概念我都懂!关键在于,那个n!=n的条件那里,我死都没懂。因为两个都是n,同样的n,不管是否一个一个线程执行holder一个没有赋值,都应该相等啊!

可能我比较笨,希望层主包含!可能我的问题也没有表述清楚。我现在很大的一个感受就是,很多时候,一个问题并不是我不知道如何解决,很多时候是我没法把一个问题表述清楚。就像这个问题一样。 --------------------编程问答-------------------- 不懂!!看一看 --------------------编程问答--------------------
引用 8 楼 yzw19932010 的回复:
谢谢啊!这么久的第一次给了我。深感荣幸!
不过,还是不很懂啊。你说的概念我都懂!关键在于,那个n!=n的条件那里,我死都没懂。因为两个都是n,同样的n,不管是否一个一个线程执行holder一个没有赋值,都应该相等啊!

可能我比较笨,希望层主包含!可能我的问题也没有表述清楚。我现在很大的一个感受就是,很多时候,一个问题并不是我不知道如何解决,很多时候是我没法把一个问题表述清楚。就像这个问题一样。


注意这句“原因就是构造函数不是原子的,指令可能重排序。”
n != n也不是原子的!
其编译后的java指令为
aload
getfield  //第一次读变量n
aload
getfield  //第二次读变量n
if_icmpeq
如果不同步的话,两次读变量n之间是可能会切换到其它线程执行的。

继续使用我给出的“不安全发布”的例子来说
第一个线程用来new实例,当第二个线程中 sh.holder!=null 时(也就是构造函数已经发布了),但因为重排序,n有可能尚未赋值,也就是int变量的默认值0。此时第一次读变量n记为n',实际值为0;此时再次切换到线程1完成了n赋值为42,此时第二次读变量n记为n'',实际值为42。
你所看到的n!=n实际是n'!=n'',最后是0!=42,结果为真
执行throw new AssertionError("This statement is false.")

要是还不明白我就无能为力了
--------------------编程问答--------------------
引用 5 楼 yzw19932010 的回复:
Quote: 引用 4 楼 pangjingji 的回复:

没搞明白为什么会有不安全发布?在构造函数中发布该对象才可能产生不安全发布,构造函数中没有发布这个对象,怎么会产生不安全发布呢? 

一个线程拿到可能是null的Holder引用,而一个可能拿到初始化好的,这样不是不安全么?
8楼说的很清楚了。 --------------------编程问答--------------------
引用 10 楼 runshine 的回复:
Quote: 引用 8 楼 yzw19932010 的回复:

谢谢啊!这么久的第一次给了我。深感荣幸!
不过,还是不很懂啊。你说的概念我都懂!关键在于,那个n!=n的条件那里,我死都没懂。因为两个都是n,同样的n,不管是否一个一个线程执行holder一个没有赋值,都应该相等啊!

可能我比较笨,希望层主包含!可能我的问题也没有表述清楚。我现在很大的一个感受就是,很多时候,一个问题并不是我不知道如何解决,很多时候是我没法把一个问题表述清楚。就像这个问题一样。


注意这句“原因就是构造函数不是原子的,指令可能重排序。”
n != n也不是原子的!
其编译后的java指令为
aload
getfield  //第一次读变量n
aload
getfield  //第二次读变量n
if_icmpeq
如果不同步的话,两次读变量n之间是可能会切换到其它线程执行的。

继续使用我给出的“不安全发布”的例子来说
第一个线程用来new实例,当第二个线程中 sh.holder!=null 时(也就是构造函数已经发布了),但因为重排序,n有可能尚未赋值,也就是int变量的默认值0。此时第一次读变量n记为n',实际值为0;此时再次切换到线程1完成了n赋值为42,此时第二次读变量n记为n'',实际值为42。
你所看到的n!=n实际是n'!=n'',最后是0!=42,结果为真
执行throw new AssertionError("This statement is false.")

要是还不明白我就无能为力了

懂了懂了!!!大哥,我就是二了一下!真的!泪崩啊!!谢谢!!! --------------------编程问答--------------------
引用 10 楼 runshine 的回复:
Quote: 引用 8 楼 yzw19932010 的回复:

谢谢啊!这么久的第一次给了我。深感荣幸!
不过,还是不很懂啊。你说的概念我都懂!关键在于,那个n!=n的条件那里,我死都没懂。因为两个都是n,同样的n,不管是否一个一个线程执行holder一个没有赋值,都应该相等啊!

可能我比较笨,希望层主包含!可能我的问题也没有表述清楚。我现在很大的一个感受就是,很多时候,一个问题并不是我不知道如何解决,很多时候是我没法把一个问题表述清楚。就像这个问题一样。


注意这句“原因就是构造函数不是原子的,指令可能重排序。”
n != n也不是原子的!
其编译后的java指令为
aload
getfield  //第一次读变量n
aload
getfield  //第二次读变量n
if_icmpeq
如果不同步的话,两次读变量n之间是可能会切换到其它线程执行的。

继续使用我给出的“不安全发布”的例子来说
第一个线程用来new实例,当第二个线程中 sh.holder!=null 时(也就是构造函数已经发布了),但因为重排序,n有可能尚未赋值,也就是int变量的默认值0。此时第一次读变量n记为n',实际值为0;此时再次切换到线程1完成了n赋值为42,此时第二次读变量n记为n'',实际值为42。
你所看到的n!=n实际是n'!=n'',最后是0!=42,结果为真
执行throw new AssertionError("This statement is false.")

要是还不明白我就无能为力了
求互相关注。还有些问题想求解脱。 --------------------编程问答--------------------
引用 13 楼 yzw19932010 的回复:
求互相关注。还有些问题想求解脱。

恩,关注了。不过不常上CSDN,有事还是给我留言吧。
补充:Java ,  Java SE
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,