【多线程与并发】:发布与逸出

目录

  • 目录
  • 发布与逸出
      • 发布一个对象
      • 内部可变状态逸出
      • this引用逸出
      • 关于发布

发布与逸出

发布(publish) 一个对象是指,使对象能够在当前作用域之外的代码中使用。当某个不应该发布的对象被发布时,这种情况就被称为 逸出(escape)

发布一个对象

public static  Set knownSecrets;//公有变量

public void initialize(){
    knownSecrets = new HashSet();
}

上述代码在initialize方法中实例化了一个HashSet对象,并将其引用保存到knownSecrets中发布了该对象(KnownSecrets被声明为公有变量,当前作用域之外的代码可以访问该对象),并且,如果将一个Secret对象添加到该集合(KnownSecrets是一个集合对象),那么同样会发布这个对象,因为任何代码都可以遍历这个集合。

内部可变状态逸出

Class UnsafeStates{
     private String[] states = new String[]{
         "AK","AL"……
     };
     public String[] getStates(){ return states};
}

上述方式发布states,使得任何调用者都可以修改这个数组的内容,因为在get方法中返回了states的引用,数组states已经逸出了它所在的作用域,这个私有变量被发布了。

this引用逸出

public class ThisEscape{
    public ThisEscape(EventSource source){
        source.registerListener(
            new EventListener(){//在new这里,就新建了一个内部类的对象,持有ThisEscape类的引用
                public void onEvent(Event e){
                    doSomething(e);
                }
            }
    );
    }
}

上述代码在发布EventListener时,也隐含的发布了ThisEscape实例本身,因为EventListener是一个非静态内部类,一个非静态内部类在编译完成后会隐含的保存一个它外围类的引用“ThisEacape.this”,然而在上述代码中,构造函数还没有完成,也就是说,ThisEscape本身还没有构造好,但是其发布的对象就已经持有了一个ThisEscape的引用。

在构造过程中使this引用逸出的一个常见的错误是,在构造函数中启动一个线程。当对象在其构造函数中创建一个线程时,无论是显式创建还是隐式创建,this引用都会被新创建的线程共享,在对象没有完全构造之前,新的线程就可以看见它

如果想要避免不正确的构造过程,可以采用工厂方法来防止this引用在构造过程中逸出。

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

关于发布

如果一个已经发布的对象,能够通过非私有的变量引用和方法调用到达其他的对象,那么这些对象也都会被发布。无论其他线程会对已发布的引用执行何种操作,其实都不重要,因为误用该引用的风险始终存在。

你可能感兴趣的:(多线程与并发)