单例模式可以算是最简单的设计模式了。它表示一个类创建一个对象。
正常来讲,对于类,我们可以new出多个对象,那么如何能够实现只允许创建一个对象呢?
我们先看一个问题,为什么可以用new来new一个类的对象呢?这是因为类中定义了public的构造函数。那么如果我们将构造函数设置为private的呢?那么就会导致一个问题,我们没有办法再通过new来new一个对象了。因为构造函数是private的了。那么什么场景下可以调用private的构造函数呢?那就是类的内部的方法。
但是还有一个问题,我们要调用类内部的方法就是首先new类的实例对象,但是new类的实例对象又因为private的构造函数而只能通过调用类的内部方法实现,……, 这就变成了一个鸡生蛋的,蛋生鸡的问题。
那么有什么方法能够直接调用类的函数呢?那就是static。
因此对于上面的问题,我们有了一个根本的解决方案
1 | public class Singleton { |
当我们需要实例化Singleton的时候,可以直接:
1 | Singleton.getInstance(); |
但是这不能解决只生成一个实例的问题,因此需要做如下的改动:
1 | public class Singleton { |
这样当我们调用Singleton.getInstance()时,如果不存在类的实例对象,就会创建,如果存在,就会直接返回之前已经创建的实例对象。这就实现了我们的单例模式。
这样就没有任何问题了么?
多线程下呢?
假设两个线程同时走到if (uniqueInstance == null) 语句呢?这必然会导致new Singleton();分别在两个线程中被执行,这破坏了我们的单例模式。因此我们需要对上面的模型进行修改
一种方式是可以通过增加关键字synchronized来解决,但是这种方式会导致每次都要用到同步,效率奇差。
1 | public class Singleton { |
另一种方式就是加锁:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public 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
8public class Singleton {
private static Singleton uniqueInstance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return uniqueInstance;
}
}
最后定义一下单例模式
单例模式
确保一个类只有一个实例,并提供一个全局的访问点