- 发布对象: 使一个对象能够被当前范围之外的代码所使用
- 对象逸出: 一种错误的发布。当一个对象还没有构造完成时,就使它被其他线程所见
package io.haitaoc.concurrency.example.publish;
public class UnsafePublish {
private String[] states = {"a","b","c"};
public String[] getStates(){
return states;
}
/**
* 通过new UnsafePublish()发布了一个UnsafePublish类的实例
* 通过实例的public方法得到了私有域states数组的引用
* 可以在其他任何线程里修改这个数组里的值
* 这样在其他线程中想使用states数组时,它的值是不完全确定的
* 因此这样发布的对象是线程不安全的,因为无法保证是否有其他线程对数组里的值进行了修改
* @param args
*/
public static void main(String[] args) {
UnsafePublish unsafePublish = new UnsafePublish();
for (String i : unsafePublish.getStates()) {
System.out.print(i+" ");
}
unsafePublish.getStates()[0] = "d";
System.out.println();
for (String i : unsafePublish.getStates()) {
System.out.print(i+" ");
}
}
}
package io.haitaoc.concurrency.example.publish;
import io.haitaoc.concurrency.annotation.NotThreadSafe;
@NotThreadSafe
public class Escape {
private int thisCanBeEscape = 0;
public Escape(){
new InnerClass();
}
/**
* 内部类的实例里面包含了对封装内容thisCanBeEscape的隐含引用
* 这样在对象没有被正确构造之前,他就会被发布,有可能有不安全的因素在
* 一个导致this引用在构造期间逸出的错误 是在构造的函数过程中启动了一个线程
* 无论是隐式的启动还是显示地启动都会造成this引用的逸出,新线程总是会在对象构造完毕
* 之前就已经看到this引用 所以要再构造函数中使用线程,就不要启动它而应该专有的start或初始化的方法来统一启动线程,
* 可以采用工厂方法和私有构造函数来完成对象创建和监听器的注册等
*
*
* 在对象未完成构造之前 不可以将其发布
*/
private class InnerClass{
public InnerClass(){
System.out.println(Escape.this.thisCanBeEscape);
}
}
public static void main(String[] args) {
new Escape();
}
}