单例模式

单例模式可以算是最简单的设计模式了。它表示一个类创建一个对象。

正常来讲,对于类,我们可以new出多个对象,那么如何能够实现只允许创建一个对象呢?

我们先看一个问题,为什么可以用new来new一个类的对象呢?这是因为类中定义了public的构造函数。那么如果我们将构造函数设置为private的呢?那么就会导致一个问题,我们没有办法再通过new来new一个对象了。因为构造函数是private的了。那么什么场景下可以调用private的构造函数呢?那就是类的内部的方法。

但是还有一个问题,我们要调用类内部的方法就是首先new类的实例对象,但是new类的实例对象又因为private的构造函数而只能通过调用类的内部方法实现,……, 这就变成了一个鸡生蛋的,蛋生鸡的问题。

那么有什么方法能够直接调用类的函数呢?那就是static。

因此对于上面的问题,我们有了一个根本的解决方案

1
2
3
4
5
6
7
public class Singleton {
private Singleton() {}

public static Singleton getInstance() {
return new Singleton();
}
}

当我们需要实例化Singleton的时候,可以直接:

1
Singleton.getInstance();

但是这不能解决只生成一个实例的问题,因此需要做如下的改动:

1
2
3
4
5
6
7
8
9
10
11
public class Singleton {
private static Singleton uniqueInstance;
private Singleton() {}

public static Singleton getInstance() {
if (uniqueInstance == null) {
return new Singleton();
}
return uniqueInstance;
}
}

这样当我们调用Singleton.getInstance()时,如果不存在类的实例对象,就会创建,如果存在,就会直接返回之前已经创建的实例对象。这就实现了我们的单例模式。

这样就没有任何问题了么?

多线程下呢?

假设两个线程同时走到if (uniqueInstance == null) 语句呢?这必然会导致new Singleton();分别在两个线程中被执行,这破坏了我们的单例模式。因此我们需要对上面的模型进行修改

一种方式是可以通过增加关键字synchronized来解决,但是这种方式会导致每次都要用到同步,效率奇差。

1
2
3
4
5
6
7
8
9
10
11
public class Singleton {
private static Singleton uniqueInstance;
private Singleton() {}

public static synchronized Singleton getInstance() {
if (uniqueInstance == null) {
return new Singleton();
}
return uniqueInstance;
}
}

另一种方式就是加锁:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Singleton {
private volatile static Singleton uniqueInstance;
private Singleton() {}

public static Singleton getInstance() {
if (uniqueInstance == null) {
synchronized (Singleton.class) {
if (uniqueInstance == null) {
return new Singleton();
}
}
}
return uniqueInstance;
}
}

还有一种方式就是在静态初始化器中创建单例,JVM在加载这个类的时候会马上创建此类的单例

1
2
3
4
5
6
7
8
public class Singleton {
private static Singleton uniqueInstance = new Singleton();
private Singleton() {}

public static Singleton getInstance() {
return uniqueInstance;
}
}

最后定义一下单例模式

单例模式

确保一个类只有一个实例,并提供一个全局的访问点

显示 Gitment 评论