【问题标题】:Volatile class instances and member access in JavaJava中的可变类实例和成员访问
【发布时间】:2014-07-19 14:49:59
【问题描述】:

我认为我正在做的事情是正确的,但如果不是这样,这可能会非常糟糕,我真的很想澄清一下。

代码是尝试表达观点的示例,如有轻微错别字,请见谅。

我有以下课程

public class Components
{
    public final String mVar1;
    public final boolean mVar2;

    public Components(String var1, boolean var2)
    {
        mVar1 = mVar1;
        mVar2 = mVar2;
    }
}

如果我创建此类的 volatile 实例,我相信将此组件的值分配给已创建并在内存中的地址是线程安全的。

public class Storage
{
    public static volatile Components sComponents = null;
}

所以,无论我是在主线程还是任何其他线程上设置这个变量(其中一个集合只是将它指向一个已经创建的对象,而不是创建一个新对象),它都应该是线程安全的,因为 volatile 关键字正在作用于 Components 引用,它将被更新为指向已经存在的对象。

所以,例如

public class ThreadedClass
{
    public ThreadedClass()
    {
        // Create an instance of Components so we have something to copy
        mInitialComponents = new Components("My String", false);

        // Spin off a thread
        create_a_new_thread( threadEntryPoint );
    }

    // This function is called every frame on the main thread
    public void update()
    {
        // If we have our components, print them out
        if (Storage.sComponents != null)
        {
            print(sComponents.mVar1);
            print(sComponents.mVar2);
        }
    }

    private Components mInitialComponents = null;

    private void threadEntryPoint()
    {
        // Just sleep for a bit so update gets called a few times
        sleep(3000);

        // Set our components
        Storage.sComponents = mInitialComponents;
    }
}

(在现实世界的代码中,mInitialComponents 是通过同步函数创建和访问的,因此访问原始对象是线程安全的)。

所以,我的问题是,当在主线程或任何其他线程上调用更新时,一旦 Storage.sComponents 已设置为 threadEntryPoint 中的现有对象,它是否只是更新对象引用,所以该对象将被保证是每当我们检查 null 时完成。

或者是否有可能正确分配了一些内部成员或没有正确分配。

谢谢

【问题讨论】:

    标签: java multithreading thread-safety volatile


    【解决方案1】:

    您的更新方法不是线程安全的,可能会引发空指针异常。这可以通过将其更改为来解决:

    // This function is called every frame on the main thread
    public void update()
    {
        final Components components = Storage.sComponents;
        // If we have our components, print them out
        if (components != null)
        {
            print(components.mVar1);
            print(components.mVar2);
        }
    }
    

    组件中的内部值可以安全使用,因为它们是最终的。这是假设您没有从其构造函数中泄漏对 Components 实例的引用。

    【讨论】:

      【解决方案2】:

      可以安全地假设如果components 不为空,则其成员变量已正确初始化。根据 Java 虚拟机规范,通过对从 new 返回的对象的引用进行的任何访问都可以保证看到该对象中任何 final 字段的完全初始化版本。请参阅 JVM 规范,章节 17.5

      【讨论】:

        最近更新 更多