即使经过this,我仍然不清楚最终的使用如何导致安全发布在下面的代码中。有人能给出一个易于理解的解释。
public class SafeListener
{
private final EventListener listener;
private SafeListener()
{
listener = new EventListener()
{ public void onEvent(Event e)
{ doSomething(e); }
};
}
public static SafeListener newInstance(EventSource source)
{
SafeListener safe = new SafeListener();
source.registerListener(safe.listener);
return safe;
}
}
答案 0 :(得分:9)
编辑添加:对origins of Java and JSR-133's final
behavior的有趣观点。
关于final
如何在新JMM中工作的规范性参考,以便安全发布:http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#finalRight
简单回顾一下,我认为你的代码代表EventSource source
对象的“安全”发布,可能会在不同的线程中向listener
发送事件回调。保证在传递的safe.listener
引用上运行的线程将看到完全初始化的listener
字段。此不进一步保证与调用onEvent
或与对象状态的其他交互相关的其他同步问题。
您的代码保证的是,当SafeListener
的构造函数在静态方法中返回引用时,listener
字段将不会在未写入状态下看到(即使没有显式同步)。例如:假设线程A调用newInstance()
,从而导致对listener
字段的分配。假设线程B能够取消引用listener
字段。然后,即使没有任何其他同步,线程B也能保证看到写listener = new EventListener()...
。如果该字段不是final
,您将无法获得该保证。有几种(其他)方式提供不同性能和可读性的保证(显式同步,使用原子引用,使用易失性)。
并非所有合法的都是可取的。建议你看一下JCiP,或许这个article on safe publication techniques。
最近的一个相关问题是:"Memory barriers and coding...","Java multi-threading & Safe Publication"。
答案 1 :(得分:4)
简而言之,final
的规范(参见@ andersoj的答案)保证当构造函数返回时,最终字段将被正确初始化(从所有线程可见)。
对于非最终字段没有这样的保证(这意味着如果另一个线程获得了新构造的对象,则该字段可能尚未设置)。
这有效是JVM规范的一部分。
它的工作原理是JVM实现细节。
答案 2 :(得分:0)
您可以参考JSL 最终字段或通过最终引用可到达的对象不能通过对该对象的引用的初始加载来重新排序。在构建之后,所有其他线程都可以看到它。