- 所谓this逃逸就是说,在类的构造方法还没执行完之前,其他线程就获得了this的引用并且去干一些事情,但是这时的对象是不完善的,可能某些字段在this逃逸后才初始化,但是其它线程已经使用这些字段了。多线程是发生this逃逸的条件,并且这种错误一旦发生就很危险,因为外界线程会访问到不完善的对象。
避免this逃逸的最好方法就是不要再构造方法中传递出this或者对象的属性。
- 实例一:t2打印出来的a为null
/**
* 解决this逃逸的最好方法就是不要在构造方法中将this传递出去
*
*/
public class ThisEscapeOne {
final String a;
static ThisEscapeOne t;
//这里将语句1写在最后也无效,因为可能会发生重排序,仍会发生逃逸
public ThisEscapeOne() {
t = this; //1
//这里延时200ms,模拟构造方法其他字段的初始化
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
a = "ABC"; //2
}
public static void main(String[] args) {
//t1进行构造对象
Thread t1 = new Thread(() -> {
new ThisEscapeOne();
});
//t2观测常量的值
Thread t2 = new Thread(() -> {
System.out.println(ThisEscapeOne.t.a);
});
t1.start();
//尽量保证t1先启动
try {
Thread.sleep(10);
} catch (InterruptedException e) {
}
t2.start();
}
}
2.示例二,打印结果为:Source:ThisEscapeTwo [id=null]
/**
* 对于相互包含的类,通过构造函数将自己set时可能会发生this逃逸
*/
public class ThisEscapeTwo {
protected final String id;
public ThisEscapeTwo(DataSource source) {
source.setThisEscapeTwo(this);
//延时100ms,可能是初始化其它字段,也可能是其他耗时的操作
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
id = "ABC";
}
public void getMessage(String mess) {
System.out.println("Source:" + mess);
}
@Override
public String toString() {
return "ThisEscapeTwo [id=" + id + "]";
}
public static void main(String[] args) {
final DataSource s = new DataSource();
Thread t = new Thread(() -> {
new ThisEscapeTwo(s);
});
t.start();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
s.postMessage();
}
}
//数据源
class DataSource {
private ThisEscapeTwo escapeTwo;
public void setThisEscapeTwo(ThisEscapeTwo escapeTwo) {
this.escapeTwo = escapeTwo;
}
public void postMessage() {
if (this.escapeTwo != null) {
escapeTwo.getMessage(escapeTwo.toString());
}
}
}
网友评论