java并发编程实践学习(4)对象的发布和逸出之this逃逸

java并发编程实践学习(四)对象的发布和逸出之this逃逸

《java并发编程实践》的第三章,对象的发布和逸出,作者提到了2种常见的对象逸出情况:在构造函数中注册事件监听,在构造函数中启动新线程。示例代码如下:

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

public class ThreadThisEscape {  
    public ThisEscape() {  
        new Thread(new EscapeRunnable()).start();  
        // ...  
    }  
      
    private class EscapeRunnable implements Runnable {  
        @Override  
        public void run() {  
            // ThreadThisEscape.this就可以引用外围类对象, 但是此时外围类对象可能还没有构造完成, 即发生了外围类的this引用的逃逸  
        }  
    }  
} 
这2种方式的共同点是:在构造函数中使用内部类,并且代码可能会出现并行。也就是说,当内部类代码执行的时候,外部类对象的创建过程很有可能还没结束,这个时候如果内部类访问外部类中的数据,很有可能得到还没有正确初始化的数据


*上http://*.com/questions/3705425/java-reference-escape讨论了this逸出的问题。下面给出2个具体点的例子,更容易理解this逸出的问题。

public class ThisEscape {
    private final int var;

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

        // more initialization
        // ...

        var = 10;
    }

    // result can be 0 or 10
    int doSomething(Event e) {
        return var;
    }
}

事件监听器一旦注册成功,就能够监听用户的操作,调用对应的回调函数。比如出发了e这个事件,会执行doSomething()回调函数。这个时候由于是异步的,ThisEscape对象的构造函数很有可能还没有执行完(没有给var赋值),此时doSomething中获取到的数据很有可能是0,这不符合预期。


public class ThreadThisEscape
{
	private int weight = 0;

	public ThreadThisEscape()
	{
		weight = 1;
		
		new Thread(new EscapeRunnable()).start();

		// 模拟构造函数耗时
		for (int i = 0; i < 1000000; i++)
		{
			
		}
		
	}

	private class EscapeRunnable implements Runnable
	{
		@Override
		public void run()
		{
			System.out.println(ThreadThisEscape.this.weight);
		}
	}

	public static void main(String[] args)
	{
		new ThreadThisEscape();
	}
}


this逃逸就是说在构造函数返回之前其他线程就持有该对象的引用,调用尚未构造完全的对象的方法可能引发错误。