当前位置: 代码迷 >> 综合 >> Java并发编程之对象发布(Publish)和逸出(Escape)
  详细解决方案

Java并发编程之对象发布(Publish)和逸出(Escape)

热度:32   发布时间:2023-10-17 09:55:08.0

什么是对象发布:

使一个对象能够被当前范围之外的代码所使用。

示例:

@Slf4j
public class UnsafePublish {private String[] states = {"a", "b", "c"};public String[] getStates() {return states;}public static void main(String[] args) {UnsafePublish unsafePublish = new UnsafePublish();log.info("{}", Arrays.toString(unsafePublish.getStates()));unsafePublish.getStates()[0] = "d";log.info("{}", Arrays.toString(unsafePublish.getStates()));}
}

通过public访问级别发布了类的域(states),在类的任何外部线程都可以访问这些域。这样的发布对象是不安全的,因为不知道其他线程是否会修改这个域。简单的说通过unsafePublish发布了这个类的实例。

 

什么是对象逸出:

一个错误的发布。当一个对象还没有构造完成时,就使它被其他线程所见。

示例:

@Slf4j
public class Escape {private int thisCanBeEscape = 0;public Escape () {new InnerClass();}private class InnerClass {public InnerClass() {log.info("{}", Escape.this.thisCanBeEscape);}}public static void main(String[] args) {new Escape();}
}

Escape类的构造方法还没有构造完成,它的内部类InnerClass就能得到该类的对象引用,称之为对象逸出。

再看另外一种逃逸:


public class Escape {private int id = 0;private String name = null;public Escape() {new Thread(new MyRun()).start();new Thread(new MyRun()).start();name = "zhangsan";}private class MyRun implements Runnable {@Overridepublic void run() {System.out.println(Escape.this.name);System.out.println(Escape.this.id);}}public static void main(String[] args) {new Escape();}

在Escape构造方法中,原本是要初始化该类的属性name的值为zhangsan,但是因为在构造方法中,启动线程,可能没有执行到name="zhangsan",线程就已经执行了,导致线程中name的值为null值。

 

解决方法:

在构造函数执行完之前,要避免使用Object.this这种引用和避免在构造函数中启动线程。

 

安全发布的四种方式:

  1. 在静态初始化函数中初始化一个对象引用
  2. 将对象的引用保持到volitale类型域中或者AtomicReference对象中
  3. 将对象的引用保存到某个正确的构造对象的final类型域中
  4. 将对象的引用保持到一个由锁保护的域中
  相关解决方案