单例模式

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

最简单的形式

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Singleton1 {
private static Singleton1 uniqueInstance;
private Singleton1() {
}
public static Singleton1 getInstance(){
if(uniqueInstance==null){
uniqueInstance=new Singleton1();
}
return uniqueInstance;
}
}

上面的代码很容易理解,将构造函数私有化,在静态方法getInstance中,如果不调用getInstance方法,那么这个对象永远不会产生,这是一个延迟实例化

在多线程状态下的问题

因为getInstance方法中的代码不是原子的。如果多个线程同时执行,则有可能产生多个实例。具体是怎样产生的看下图

多线程情况下获取到多个实例
多线程情况下获取到多个实例

使用同步处理多线程产生多个实例的问题

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Singleton2 {
private static Singleton2 uniqueInstance;
private Singleton2() {
}
public static synchronized Singleton2 getInstance(){
if(uniqueInstance==null){
uniqueInstance=new Singleton2();
}
return uniqueInstance;
}
}

只需要在getInstance方法声明中添加 synchronized 关键字就可以解决问题,保证在多线程情况下,同时只有一个线程能够进入这个方法
但是同步会降低性能,每次调用getInstance方法的时候,都需要同步,但是我们在获取到第一个实例之后,后面都不需要同步。在方法声明上设置同步是一种累赘

改善方法同步的问题

如果引用性能问题不是很关键那么什么都别做

使用饿汉式创建单例模式

1
2
3
4
5
6
7
8
9
10
public class Singleton3 {
private static Singleton3 uniqueInstance =new Singleton3();
private Singleton3() {
}
public static Singleton3 getInstance(){
return uniqueInstance;
}
}

使用饿汉式创建单例,JVM在加载这个类时间,马上创建唯一的单例实例,保证在任何线程访问uniqueInstance静态变量之前,一定要先创建此实例。

懒汉式双重检查锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Singleton4 {
private volatile static Singleton4 uniqueInstance;
private Singleton4() {
}
public static Singleton4 getInstance(){
if(uniqueInstance==null){
synchronized (Singleton4.class) {
if (uniqueInstance==null) {
uniqueInstance = new Singleton4();
}
}
}
return uniqueInstance;
}
}

利用双重检查锁,首先检查实例是否已经创建,,如果尚未创建,才进行同步,这样一来,只会有第一次会同步,如果性能是你关心的重点,那么这个做法可以帮你打打减少getInstance的时间耗费
注意 这里使用了 volatile 保证在多线程的情况下,对修饰的对象的内存可见性。