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

什么是对象发布:

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

示例:

@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 {
		@Override
		public 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. 将对象的引用保持到一个由锁保护的域中

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