• 局部创建对象(不正确发布:当好对象变坏时)


    局部创建对象(不正确发布:当好对象变坏时)

    代码清单1:

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

    代码清单2:

    public 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.");  
        }  
    } 

      你无法信赖局部创建对象,一个监视着处于不一致状态对象的线程,会看到尽管该对象自发布之后从未修改过,但是它的状态还是会发生突变。代码清单2的Holder使用代码清单1的不安全发布方式发布,那么除了发布线程,其他线程调用assertSanity时都可能抛出AssertionError异常。(此问题并非出在Holder类自身,而是Holder没有被正确的发布,但是将域n声明为final类型,使Holder成为不可变的,可以避免出现不正确发布的问题)

      原因分析如下:

      因为没有同步来确保Holder对其他线程可见,所以我们称Holder是“非正确发布的”,没有正确发布的对象会导致两种错误。首先,发布线程以外的任何线程都可以看到Holder域的过期值,因而看到的是一个null引用或者旧值,即使此刻Holder已经被赋与新值。其次,更坏的情况是,线程看到的Holder引用是最新的,然而Holder状态却是过期的。 (可以看出,在构造函数中设置的域值,应该是向这些域写入的第一个值,因此,没有“更旧”的值可以作为所谓的过期值。但是Object的构造函数会先于子类的构造函数运行,并首先向所有的域写入默认值。因此这些默认值可能成为域的过期值) 这使程序执行变得更加不可预测:线程首次读取某个域可能会看到过期值,再次读取该域会得到一个更新值,这正是assertSanity抛出AssertionError异常的原因。

    不可变对象与初始化安全性

      出于不可变对象的重要性,java存储模型为共享不可变对象提供了特殊的初始化安全性的保证。

       

      另一方面,即使发布对象引用时没有使用同步,不可变对象仍然可以被安全地访问。为了获得这种初始化安全性的保证,应该满足所有不可变性的条件:不可修改的状态,所有域都是final类型的以及正确的构造。

      不可变类的例子:

    public class Holder {  
        private final int n;  
      
        public Holder(int n) {  
            this.n = n;  
        }  
      
        public void assertSanity() {  
            if (n != n)  
                throw new AssertionError("This statement is false.");  
        }  
    }  

      注意:不可变对象可以在没有额外同步的情况下,安全地用于任意线程;甚至发布它们时也不需要同步。

      这个保证还会延伸到一个正确创建的对象中的所有final类型域的值,没有额外的同步,final域也可以被安全地访问。然而,如果final域指向可变对象,那么访问这些对象的状态时仍然需要同步。

    以上内容出  <<自并发编程实践>>

  • 相关阅读:
    Java实现 蓝桥杯 历届试题 城市建设
    Java实现 蓝桥杯 历届试题 城市建设
    Java实现 蓝桥杯 历届试题 城市建设
    Java实现 蓝桥杯 历届试题 城市建设
    MYSQL创建数据库时候直接指定编码和排序规则
    Eclipse 安装插件(aptana、svn 、git、Java EE、JSHint)
    (转)CentOS无损调整磁盘分区大小的实现方法
    Linux(centos)系统各个目录的作用详解
    Ecplise插件安装方法
    Fedora25 将eclipse的快捷方式添加到Applications中
  • 原文地址:https://www.cnblogs.com/mjorcen/p/4222222.html
Copyright © 2020-2023  润新知