这两个单例实现有什么区别?

时间:2017-06-18 05:01:35

标签: java c# singleton

我有问题要理解这两个功能之间的区别。他们都实行单身人士吗?如果是这样的话,彼此之间的利益是什么。

    private static GameManager instance = new GameManager();
    public static GameManager get(){return instance;}

和吼叫,

    private static GameManager _instance;

    public static GameManager instance(){
     if(_instance == null) _instance = new GameManager();
     return _instance;
    }

4 个答案:

答案 0 :(得分:3)

它们都是单例实现的。唯一的区别是Singleton实例在第二个代码块上是Lazy Loaded。换句话说,在调用instance方法之前,它不会初始化GameManager。

您还应该将代码重写为更简单的代码,例如:

public static GameManager instance = new GameManager();

答案 1 :(得分:3)

不同之处在于第二个实现在多线程环境中并不好,因为它可能会创建多个实例。这是两个线程同时检查实例== null并且都是真的

请注意,选项#1也可能是惰性的:

class GameManager {
    private static GameManager instance = new GameManager();

    private GameManager() {
        System.out.println("instance created");
    }

    public static GameManager getInstance() {
        return instance;
    }
}

尝试使用它,您将看到只有在我们第一次调用getInstance()

时才创建实例

答案 2 :(得分:3)

主要有两点不同:

  1. 第一个例子是"渴望",第二个例子是"懒惰"。具体来说,第一个将在单例类初始化时创建单例对象,但第二个将在第一次调用changelog时创建单例对象。

    一般来说,渴望初始化更简单。但是,初始化可能依赖于其他事情, lazy 初始化提供了一种方法来推迟初始化,直到它们发生。

  2. 如果两个或多个线程可以同时调用instance(),则第二个示例存在一个潜在的问题。具体来说,两个线程可能获得不同的instance()个对象。

    第一个示例中的类似问题是,如果应用程序中的类初始化周期存在问题,则一个线程可能会看到GameManager。这可能导致一个线程看到null值。但是,类初始化的语义意味着类初始化和调用初始化类上的方法之间存在发生之前的关系。因此,保证两个线程都能看到null对象的正确初始状态。

  3. 但请注意"双重检查锁定"示例仅适用于Java 5及更高版本。

    如果有多个线程共享GameManager实例,则很可能需要执行其他操作才能使应用程序(始终)正常运行。

答案 3 :(得分:-1)

第一个实现比多个处理器中的其他实现更安全。以下规则保证了第一个实现安全的原因。

因为JLS 17.4.5 Happens-before Order定义了一条规则:

任何对象的默认初始化发生在任何其他操作之前(其他操作) 一个程序的默认写入。

所以当有人调用方法get()时,instance字段会被完全初始化。

第二个实现称为lazy initialization,但它在您的代码中没有正确。为了更快地启动程序,如果lazy initialization的初始化,使用GameManager是一个很好的做法花更多的时间。

通过第二种实现,有两种方法可以使GameManager线程安全。

首先,您可以使方法instance同步,就像这样:

public synchronized static GameManager instance(){
 if(_instance == null) _instance = new GameManager();
 return _instance;
}

但它不是高性能,因为每个调用都将在多个线程中与该方法同步。

第二,您可以在方法double-check中使用instance,并将_instance声明为volatile ,代码如下:< / p>

static volatile GameManager _instance;

public static GameManager instance(){
    if (_instance== null) {              //0
        synchronized(GameManager.class) {  //1
            if (_instance == null)          //2
                _instance= new GameManager ();  //3
        }
    }
    return o;
}

此实现代码正确且高性能。

为什么field _ instance should be volatile?Let's see the following situation with no volatile ,there is two thread called thread1 and thread2 ,and they all call the method instance()`:

  1. thread1在上面代码中的第0点运行
  2. thread1检查_instance为空并且没有人获得锁定,因此它可以在第3点运行
  3. 使用代码_instance= new GameManager (); //3,由于Java Memory Model中的指令重新排序,JVM可以在'解释器or jit .Let's see what happened with that code.The instance has been constructed,the constructing instance is just be allocated memory(like the instruction中分解指令顺序新的in JVM ,not in JAVA ),and then assign to the field _ instance`,但请注意实例未完全初始化(例如,不使用默认值初始化字段,或调用静态构造方法);
  4. thread2在0点运行,并检查_instance是否为空,因此它将返回未完全初始化的_instance,并且实例不是 值得信赖的。
  5. 如果_instancevolatile,则volatile可以保证_instance在有人阅读时17.4.5 Happens-before Order完全初始化。请参阅{{1}},有一条规则:

    在每次后续读取之前发生对易失性字段(第8.3.1.4节)的写入 field.t

相关问题