我知道您可以通过写入对 final
或 volatile
字段的引用来安全地发布一个非线程安全对象,该字段稍后将由另一个线程读取,前提是在发布时,创建对象的线程会丢弃对它的引用,这样它就不会再干扰或不安全地观察对象在其他线程中的使用。
但是在这个例子中,没有显式的 final
字段,只有 final
局部变量。 如果调用者放弃对 unsafe
的引用,这个发布是安全的吗?
void publish(final Unsafe unsafe) {
mExecutor.execute(new Runnable() {
public void run() {
// do something with unsafe
}
}
}
我找到了一些问答,比如 this one ,这表明 final
局部变量被隐式“复制”到匿名类中。那是否意味着上面的例子等同于此?
void publish(final Unsafe unsafe) {
mExecutor.execute(new Runnable() {
final Unsafe mUnsafe = unsafe;
public void run() {
// do something with mUnsafe
}
}
}
编辑澄清:
Unsafe
可以是任何东西,但可以这样说:
public class Unsafe {
public int x;
}
而mExecutor
是满足Executor
契约的任何东西。
最佳答案
虽然,诚然,我并不完全确定我是否理解了您问题的真正要点,并且(正如评论中指出的那样)这个问题在您的特定情况下可能并不是真正的问题,也许可以从测试/示例中获得相关见解
考虑以下类(class):
import java.util.concurrent.ExecutorService;
class Unsafe
{
}
class SafePublication
{
private final ExecutorService mExecutor = null;
public void publish(final Unsafe unsafe)
{
mExecutor.execute(new Runnable()
{
@Override
public void run()
{
// do something with unsafe
System.out.println(unsafe);
}
});
}
}
编译得到两个.class
文件:
SafePublication.class
内部类的SafePublication$1.class
反编译内部类的类文件会产生以下内容:
class SafePublication$1 implements java.lang.Runnable {
final Unsafe val$unsafe;
final SafePublication this$0;
SafePublication$1(SafePublication, Unsafe);
Code:
0: aload_0
1: aload_1
2: putfield #1 // Field this$0:LSafePublication;
5: aload_0
6: aload_2
7: putfield #2 // Field val$unsafe:LUnsafe;
10: aload_0
11: invokespecial #3 // Method java/lang/Object."<init>":()V
14: return
public void run();
Code:
0: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
3: aload_0
4: getfield #2 // Field val$unsafe:LUnsafe;
7: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
10: return
}
可以看到对于final
参数,确实在这个类中引入了一个字段。这个字段是 val$unsafe
,它是一个 final field in the class file sense , 并在构造函数中初始化。
(这并不完全等同于您发布的第二个代码片段,因为第二个代码片段包含两个 final 字段,并且它们都使用相同的值初始化。但是关于安全问题发布,效果应该是一样的)。
关于java - 本地最终引用的安全发布,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38251982/