java - 本地最终引用的安全发布

标签 java multithreading safe-publication

我知道您可以通过写入对 finalvolatile 字段的引用来安全地发布一个非线程安全对象,该字段稍后将由另一个线程读取,前提是在发布时,创建对象的线程会丢弃对它的引用,这样它就不会再干扰或不安全地观察对象在其他线程中的使用。

但是在这个例子中,没有显式的 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/

相关文章:

multithreading - Azure 服务 TopicClient 线程安全且可重用吗?

Java 最终版和安全发布

java - 在此类的对象上调用 start() 安全吗? Java 并发实践中的一个例子

java - 没有状态的对象在发布时总是可见吗?

java - 我怎样才能颠倒这个正则表达式?

java - 安卓分钟:seconds

Java程序创建文件,但文件不可见

Java,数据库——既然我们可以直接将它放入数据库,为什么还要分配一个新对象

c# - 将单线程应用程序迁移到多线程、并行执行、蒙特卡洛模拟

multithreading - 为什么这个变量定义意味着静态生命周期?