《java并发编程实战》读书笔记二 对象的发布与逸出

概念

发布(publishing):

发布一个对象的意思是使它能够被当前范围之外的代码所使用。比如将一个引用存储到其他代码可以访问的地方,在一个非私有的方法中返回这个引用,也可以把它传递到其他类的方法中。

逸出(escape):

一个对象在尚未准备好时就将它发布,这种情况称作逸出。

逸出的方式

上边关于逸出的概念讲述的很是模糊,下面列举几个逸出的示例。

第一,通过静态变量引用逸出

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

上边代码示例中,调用initialize方法,发布了knowSecrets对象。当你向knowSecrets中添加一个Secret时,会同时将Secret对象发布出去,原因是可以通过遍历knowSecrets获取到Secret对象的引用,然后进行修改。

第二,通过非静态(私有)方法

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

以这种方式发布的states会出问题,任何一个调用者都能修改它的内容。数组states已经逸出了它所属的范围,这个本应该私有的数据,事实上已经变成共有的了。

第三,this逸出

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

在上边代码中,当我们实例化ThisEscape对象时,会调用source的registerListener方法时,便启动了一个线程,而且这个线程持有了ThisEscape对象(调用了对象的doSomething方法),但此时ThisEscape对象却没有实例化完成(还没有返回一个引用),所以我们说,此时造成了一个this引用逸出,即还没有完成的实例化ThisEscape对象的动作,却已经暴露了对象的引用,使其他线程可以访问还没有构造好的对象,可能会造成意料不到的问题。

通过上述示例,个人理解,对逸出的概念应该定义为:

一个对象,超出了它原本的作用域,而可以被其它对象进行修改,而这种修改及修改的结果是无法预测的。换句话说:一个对象发布后,它的状态应该是稳定的,修改是可被检测到的。如果在其它线程修改(或做其它操作)一个对象后导致对象的状态未知,就可以说这个对象逸出了。

总之,一个对象逸出后,不论其它线程或对象是否使用这个逸出的对象都不重要,重要的是,被误用及被误用后的未知结果的风险总是存在的。

P.S.

书中给出了避免this逸出的方法:

public class SafeListener {
  private final EventListener listener;

  private 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;
  }
}

在这个构造中,我们看到的最大的一个区别就是:当构造好了SafeListener对象之后,我们才启动了监听线程,也就确保了SafeListener对象是构造完成之后在使用的SafeListener对象。

对于这样的技术,书里面也有这样的注释:
具体来说,只有当构造函数返回时,this引用才应该从线程中逸出。构造函数可以将this引用保存到某个地方,只要其他线程不会在构造函数完成之前使用它。

你可能感兴趣的:(Java,并发)