代码之家  ›  专栏  ›  技术社区  ›  Prateek

实例为volatile的Singleton类是否会再次加载实例数据?

  •  2
  • Prateek  · 技术社区  · 11 年前

    在我的应用程序中,服务器类是在启动时加载的,所有数据都被缓存一次。

    它是一个单例类,它的实例变量是不可变的 即

    private static Server server;
    

    在findbug分析之后,我必须将其更改为volatile,(如下所示

    public class Server {
        private static volatile Server server;
    
        public static Server getInstance(){
            if(server == null){
                synchronized (Server .class) {
                    if(server == null){
                        try {
                            server = new Server(....);
                        } catch (IOException e) {
                            Logger.logError(MODULE, "IO Error while creating server home location: " + strServerHome);
                            Logger.logTrace(MODULE, e);
                        }
                    }
                }
            }
            return server;
        }.......
    }
    

    但在阅读了关于volatile on stack的文章和问答后,我认为volatile会使数据再次加载,因为volatile使信息从内存而不是缓存中读取。

    那么,我应该让我的变量不稳定吗?

    如果不稳定,那么我是否必须再次加载我的数据&再一次

    2 回复  |  直到 11 年前
        1
  •  1
  •   John B    11 年前

    通常认为创建线程安全单例最安全的方法是将其作为枚举。。。

     public enum Server{
          INSTANCE;
    
          public Server getInstance(){
              return INSTANCE;
          }
    
          private Server(){...}
    
          // rest of methods here
     }
    

    Singleton:The Enum Way

        2
  •  0
  •   isnot2bad    11 年前

    是-没有 volatile ,您得到了臭名昭著的“双重检查锁定”反模式,它不是线程安全的(即使看起来是这样)。(即使使用volatile,使用Java 5之前的Java版本也不会是线程安全的)。

    http://en.wikipedia.org/wiki/Double-checked_locking#Usage_in_Java

    但是,尽管它的线程安全 不稳定的 以及Java>=5,它的代码很难看,所以你应该写一个简单的 synchronized getInstance 方法

    public static synchronized Server getInstance() {
        if (server == null) server = new Servier(...);
        return server;
    }
    

    …或使用初始值设定项按需持有者模式:

    private static class ServerHolder {
        public static Server server = new Server(...);
    }
    
    public static Server getInstance() {
        return ServerHolder.server;
    }
    

    因为阶级 ServerHolder 在第一次使用时延迟加载,类初始化将在 获取实例 是第一次被调用,因此, Server 将被惰性地实例化。