• JAVA并发编程学习笔记------对象的可见性及发布逸出


    一、非原子的64位操作:

      当线程在没有同步的情况下读取变量时,可能会得到一个失效值,但至少这个值是由之前某个线程设置的值,而不是一个随机值,这种安全性保证被称为最低安全性。最低安全性适用于绝大多数变量,但存在一个例外:非volatile类型的64位数值变量(double,long),Java内存模型要求,变量的读取和写入操作都必须是原子操作,但对于非volatile型的long,double变量,JVM允许将64位的读操作或写操作分解为两个32位的操作,当读取一个非volatile类型的long变量时,如果对该变量的读操作和写操作在不同的线程中执行,那么很可能会读取到某个值的高32位和另一个值的低32位。

      因此,即使不考虑失效数据问题,在多线程中使用共享且可变的long,double等类型的变量也是不安全的,除非用关键字volatile来声明它们,或者用锁保护起来。

    二、volatile变量

      当把变量声明为volatile类型后,编译器与运行时都会注意到这个变量时共享的,因此不会将该变量上的操作与其他内存操作一起重排序。volatile变量不会被缓存在寄存器或者对其他处理器不可见的地方,因此在读取volatile类型的变量时总会返回最新写入的值。
      volatile局限在于:它通常用于某个操作完成、发生中断或者状态的标志,其语义不足以确保递增操作的原子性,除非你能确保只有一个线程对变量执行写操作。
      当且仅当满足以下条件时,才应该使用volatile变量:
      1)对变量的写入操作不依赖变量的当前值,或者能确保只有单个线程更新变量的值;
      2)该变量不会与其他状态变量一起纳入不变性条件中;
      3)在访问变量时不需要加锁。

      加锁机制既可以确保可见性又可以确保原子性,而volatile变量只能确保可见性。

    三、对象的发布逸出

    如下示例代码:

    public class ThisEscape {
        public ThisEscape(EventSource source) {
            source.registerListener{
                new EventListener()(){
                    public void onEvent(Event e){
                        doSomething(e);
                    }
                }
            }
        }
    }
    

      这个类不是安全的,因为当ThisEscape发布EventListener时,也隐含的发布了ThisEscape实例本身,因为这个内部类的实例中包含了对ThisEscape实例的隐含应用,因此,当从对象的构造函数中发布对象时,只是发布了一个尚未构造完成的对象。即使发布对象的语句位于构造函数的最后一行也是如此。在构造函数中创建线程并没有错误,但最好不要立即启动它,而是通过一个start或initialize方法来启动,例如,可通过如下工厂方法来防止this引用在构造过程中逸出:

    public class SafeListener {
        private static final EventListener listener;
        public SafeListener() {
            listener = new EventListener() {
                public void onEvent(Event e){
                    doSomeThing(e);
                }
            }
        }
        public static SafeListener newInstance(EventSource source){
            SafeListener safe = new SafeListener();
            source.registerListener(safe.listener);
            return safe;
        }
    }
    

      

  • 相关阅读:
    访问者模式(Visitor)
    策略模式
    职责链模式(Chain of Responsibility)
    模版方法模式
    逃离大厦第80关与马踏棋盘
    结合JDK源码看设计模式——迭代器模式
    Java并发——线程介绍
    结合JDK源码看设计模式——模板方法模式
    结合JDK源码看设计模式——桥接模式
    结合JDK源码看设计模式——组合模式
  • 原文地址:https://www.cnblogs.com/hunterCecil/p/5727913.html
Copyright © 2020-2023  润新知