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

时间:2015-08-13 09:48:46

标签: java multithreading spring concurrency volatile

我有一个类似于以下内容的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进行执行。
    • 只要最终读取器线程开始获取最新的Config对象,
    • 就不关心它是否接收到当前执行的陈旧Config对象实例。

我的问题是,如果我将私有conf字段标记为 volatile ,或者可能为conf字段添加某种同步?

我担心的是以下情况:Updater线程更新conf引用,但更改永远不会对读者线程可见。

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

2 个答案:

答案 0 :(得分:0)

此处存在的危险,无论您使用volatile还是使用@ sibnick的建议并使用ReaderWriterLock,都会有一些线程会保留自己对Config对象的私有引用延长的时间:

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

不可能编写某些时段保留自己的私有引用的代码,因为无论你对{做什么' {1}}对象,您将在互斥锁的外部执行此操作。

即使你只写这个:

Config

有些其他线程可能会在doSomethingWith(foo.getConfig()); 来电和foo.getConfig()来电之间更新配置。

您的计划必须能够应对这种可能性。

答案 1 :(得分:0)

Volatile =尝试其他线程看到对象状态的更新值,并不保证线程同步运行,并且不会进入其他线程(线程)工作的情况< / p>

内部锁定同步代码=尝试确保线程看到更新的对象状态+保证线程同步工作并相互等待,以免干扰。

此处定义的加载方法将具有非原子的指令集。它读取文件并更新加载的对象。

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

由于您的行为不是单一的陈述/分配程序,易失性不是您问题的可靠解决方案。

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

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();
        }
    }

}