当前位置: 首页 > 知识库问答 >
问题:

在Java中将对象引用声明为volatile?

微生宝
2023-03-14

我有一个Spring bean类,看起来类似于以下内容:

@Component
public class Foo{

    private Config conf;

    @PostConstruct
    public void init(){
        conf = ConfigFileLoader.loadConfigFromFile();
    }

    public Config getConfig(){
        return conf;
    }

    public void updateConfig(){
        conf = ConfigFileLoader.loadConfigFromFile();
    } 

}

ConfigFileLoader.loadConfigFromFile()从文件中读取配置并返回一个新的Config对象。

有两种类型的线程:

  • 更新程序线程:
    • 只有一个
    • 它定期调用updateConfig(当它检测到配置文件上的更改时)
    • 多个线程正在使用Config对象调用getConfig以执行
    • 只要最终读卡器线程开始获取最新的配置对象,它就不在乎是否接收到当前执行的过时配置对象实例

    我的问题是,我应该将私有conf字段标记为volatile,还是为conf字段添加某种同步?

    我担心的是以下情况:更新程序线程更新conf引用,但更改从未对读者线程可见。

    任何帮助或解释将不胜感激。

共有2个答案

端木望
2023-03-14

Volatile=试图让其他线程看到对象状态的更新值,但不保证线程同步运行,并且不会进入另一个线程无法正常工作的情况

通过内部锁同步代码=试图确保线程看到更新的对象状态保证线程同步工作并等待彼此,以便没有干扰。

您在这里定义的加载方法将具有非原子的指令集。它读取文件并更新加载到的对象。

单线程的读取和更新操作必须以一种没有其他线程干扰并尝试重做的方式在一次运行中完成。

Volatile不是解决问题的可靠方法,因为您的操作不是一个单独的声明/分配过程。

建议在对象上使用:内部锁,并使读取和更新方法同步工作。

public class Foo{

    private Config conf;
    Object lock = new Object();
    @PostConstruct
    public void init(){
        synchronize(lock)
        {
            conf = ConfigFileLoader.loadConfigFromFile();
        }
    }

    public Config getConfig(){
        synchronize(lock)
        {
            return conf;
        }
    }

    public void updateConfig(){
        synchronize(lock)
        {
            conf = ConfigFileLoader.loadConfigFromFile();
        }
    }

}
卜方伟
2023-03-14

这里的危险在于,无论您是使用易失性,还是接受@sibnick的建议并使用ReaderWriterLock,一些线程将在很长一段时间内保留对Config对象的私有引用:

 Config myConfig = foo.getConfig();
 while (conditionThatWillBeTrueForManyIterations()) {
      doSomethingWith(myConfig);
 }

编写一段时间内不保留自己私有引用的代码是不可能的,因为无论您对Config对象做什么,都是在互斥对象之外进行的。

即使你只写了这个:

doSomethingWith(foo.getConfig());

有可能其他线程会在foo.getConfig()调用和do某物with(...)调用之间更新配置。

你的程序必须能够应对这种可能性。

 类似资料:
  • 问题内容: 有人可以澄清以下代码的重要性吗? 对象A被声明为final,但是我可以更改该对象的实例变量的值,也可以获取更新后的值。那么将对象声明为final的意义何在?我知道将原始数据类型声明为final,这使该变量保持不变。 问题答案: ob将无法引用任何其他对象:final关键字。 不能重新分配。但是您可以更改其内部(如果它是原始的,则是可变的)。所以这有效: 但这不是:

  • 问题内容: 如何将类属性声明为对象? 我试过了: 但这没有用。另外,为什么要那样做呢? 只实例化该对象并使用其成员不是更好吗? 问题答案: 从有关类属性的PHP手册(重点是我的): 类成员变量称为“属性”。您可能还会看到使用其他术语(例如“属性”或“字段”)来引用它们,但是出于参考目的,我们将使用“属性”。它们是使用关键字public,protected或private之一定义的,后跟普通变量声明

  • 问题内容: 我有一些经常被调用的代码,将它们声明为变量是否有意义? 在这种情况下将动态参数传递给方法是否安全? 问题答案: 不, 它们不是线程安全的,请改用Joda-time的版本 。 或者将它们包装在同步方法中,并使其成为线程安全的 博士说得很清楚 日期格式不同步。建议为每个线程创建单独的格式实例。如果多个线程同时访问一种格式,则必须在外部进行同步。

  • 问题内容: 我想问一个关于Java的问题。我有一个用户定义的对象类,学生,它有2个数据成员,名称和ID。在另一个类中,我必须声明那个object [](例如)。但是,我不知道对象数组的大小。是否可以声明对象数组但不知道大小?谢谢。 问题答案: 用户。添加新元素时,它将自动扩展。以后,您可以根据需要将其转换为数组。 作为另一个选择(不确定您到底想要什么),您可以声明字段,而不必立即对其进行初始化。

  • 问题内容: 如果语言设计者仅使用Enum 会对语言产生怎样的影响? 现在唯一的区别是有人会写 A扩展 但由于Java中不允许扩展枚举,因此仍然是非法的。我也在考虑为jvm提供一个字节码,该字节码将smth定义为扩展枚举-但是泛型不会受到影响,因为它们都被删除了。 那么,声明的重点是什么? 谢谢! 编辑 为简单起见,让我们来看一个例子: 这个类结构有什么问题?限制可以做什么? 问题答案: 这是一个普

  • 问题内容: 当我得知该类在Java中被声明为final时,我想知道为什么会这样。那时我没有找到任何答案,但是这篇文章:如何在Java中创建String类的副本?让我想起了我的疑问。 当然,String提供了我所需要的所有功能,而且我从未想过需要扩展String类的任何操作,但是您仍然永远不会知道有人可能需要什么! 那么,有谁知道设计师决定将其定稿时的意图是什么? 问题答案: 将字符串实现为不可变对