这是确保Java中只存在一个对象实例的有效方法吗?

这是确保Java中只存在一个对象实例的有效方法吗?,java,singleton,Java,Singleton,我在Mongodb中遇到了一些奇怪的错误,在Mongodb中,您应该维护Mongosingleton。我只是想确保这是事实 public class DBManager { public static Mongo mongoSingleton = null; public static synchronized void getMongo(){ if(mongoSingleton == null){ mongoSingleton =

我在Mongodb中遇到了一些奇怪的错误,在Mongodb中,您应该维护
Mongo
singleton。我只是想确保这是事实

public class DBManager {
    public static Mongo mongoSingleton = null;

    public static synchronized void getMongo(){
         if(mongoSingleton == null){
              mongoSingleton = new Mongo();
         }
         return mongoSingleton;
    }
}

谢谢

否,因为对静态成员的公共访问可以将其更改为您想要的任何实例

更明智的做法是:

public class DBManager {
    private static Mongo mongoSingleton = new Mongo();

    public static Mongo getMongo(){
       return mongoSingleton;
    }
}
需要

 private static Mongo mongoSingleton = null;

除此之外,看起来不错。

是的,但您不希望您的成员变量是公共的。用户必须通过同步getter来访问,您必须将公共成员mongoSingleton设置为private,并隐藏默认构造函数

所以

Mongo类的实现

public class Mongo {
    private static volatile Mongo instance;
    private Mongo() {
        ...
    }

    public static Mongo getInstance() {
        if (instance == null) {
            synchronized (Mongo.class) {
                if (instance == null) { // yes double check
                    instance = new Mongo();
                }
            }
        }

        return instance;
    }
}
用法


如果您愿意放弃延迟初始化,最便宜的方法就是在对象创建时创建单例

public final class DBManager {
    private static final Mongo mongoSingleton = new Mongo();

    private DBManager() {}

    public static Mongo getMongo() {
         return mongoSingleton;
    }
}
这样,您就可以避免不必要的同步开销,否则每次调用此方法时都会出现这种开销。只要Mongo本身是线程安全的,getMongo()方法也是线程安全的


阅读Sun developer关于Singleton的文章。

如果您想了解更多详细信息,请一步一步-您可以按照以下链接进行操作-

下面是一个如何正确操作的示例

public class DBManager {
private static Mongo mongoSingleton = null;

public static synchronized void getMongo(){
     if(mongoSingleton == null){
          mongoSingleton = new Mongo();
     }
     return mongoSingleton;
}

}

只需使用
私有静态final Mongo Mongo=new Mongo()
,甚至使用
公共静态final Mongo Mongo=new Mongo()
。这是典型的单例模式,但Java中首选的方法是创建枚举:

public enum DBManager {
    INSTANCE;

    // implementation here
}
然后,您可以通过以下方式引用该实例:

DBManager.INSTANCE

请注意,在任何一种情况下(enum或singleton模式),结果都是每个类加载器一个实例,而不是每个JVM。

对singleton的延迟初始化估计过高,通常没有必要,并且会导致更多的问题。使整个静态getter
同步的简单方法是不好的,因为同步(这是一个耗时的操作)每次都执行

有一些更好的方法,如双重检查锁定或按需初始化持有者,但最简单且通常最好的方法是Josh Bloch的枚举方法。使用单个元素创建一个
enum
,您将获得一个防弹单例

public enum Mongo {

    INSTANCE;

    // instance fields, methods etc. as in any other class
}
这在功能上几乎相当于一个具有
private
构造函数和
public static final
实例的类,但它具有更简洁的语法和一些良好的隐藏特性,这些特性可以毫不动摇地保证,即使通过反射或反序列化,也不能创建任何其他实例

值得注意的是,这种方法不一定是急切的。单个
实例
是在
Mongo
类加载到JVM之后创建的,这不会在程序启动后立即发生-实际上它发生在第一次引用类时-在本例中,这意味着第一次访问
静态
字段或方法时

如果除了
实例
之外没有其他静态字段,也没有静态方法,那么在第一次访问
实例
之后,就会发生这种情况:

Mongo mongo = Mongo.INSTANCE;

换句话说,它实际上是懒惰的,没有显式同步的问题。

代码不编译-getMongo()返回void。这就是说,这是一种有效的单例模式,但查找双重检查锁定速度较慢。单例字段应该是私有的。我将保存@ptyx,但请记住,变量必须是可变的,这样才能正确工作。如果您只需要对象的一个实例,只需创建它的一个实例。需要这种模式意味着其他代码在DBManager上调用静态方法,而不是传递DBManager的实例。这甚至不是延迟初始化,除非在初始化singleton之前要调用的类上有静态方法(或者出于某种原因正在用反射加载类)。否则,只有在您第一次访问singleton时才会加载该类,这在几乎所有情况下都是懒惰的。一个次要的副作用是,每当加载该类时都会调用它,因此,如果您有任何东西加载DBManager.class而不想初始化singleton,那么它可能会很昂贵。这是唯一一个rue确保每个运行时只存在一个变量实例的方法。@LINEMAN78:这是一本经典教科书中的单例,但它肯定不是实现它的唯一方法,实际上也不是最好的方法。您还可以创建一个工厂类,这样getInstance就不必为了线程安全而同步,而是仍然延迟实例化:public final类Mongo{private Mongo(){}private final MongoFactory{private static final Mongo INSTANCE=new Mongo();}public static Mongo getInstance(){return MongoFactory.INSTANCE;}@Natix同意,有大量的论据表明enum模式是最好的,但是它假设Mongo没有扩展任何东西。我应该说,到目前为止建议的唯一答案真正保证了一个实例,因为上面的enum示例不适用于Mongo类。这很有趣,我以前从未见过。IIRC这只适用于无状态类,因为枚举不能有字段,对吗?当然,枚举可以有字段。例如:public enum DBManager{INSTANCE(new ManagableEntity());private final ManagableEntity;private DBManager(final ManagableEntity){this.entity=entity;}public ManagableEntity getEntity(){return entity;}}}的确如此。我不知道这一点,我希望我仍然不知道。这种构造似乎没有任何合理的理由,因为它只是语法上的
DBManager.INSTANCE
public enum Mongo {

    INSTANCE;

    // instance fields, methods etc. as in any other class
}
Mongo mongo = Mongo.INSTANCE;