设计模式-单例模式

内容概要: 设计模式之单例模式

单例模式确保一个类只有一个实例,并且提供一个全局的访问点。
如何确保一个类只有一个实例存在?实际上可以使用静态变量做到,但是它有个缺点: 你必须在类加载阶段就创建好对象,万一这个对象非常消耗资源,而且程序在执行过程中又没有用到,这就造成了浪费。


1. Simple Singleton: 经典的单例模式

实现非常简单,使用就如同静态变量一样,多了一个优点:延迟实例化
但是在多线程条件下,不能够保证只有一个实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package build.singleton;

/**
* A simple singleton pattern. To ensure this class has only one class, and
* offer a global access point. 此例子证明了经典的单例模式其实还是不能够保证“只有一个实例”
*
* @author jay
*
*/
public class SimpleSingleton{
// SimpleSingleton has an instance : uniqueInstance
private static SimpleSingleton uniqueInstance;

// this constructor can only be invoked in its own class
private SimpleSingleton(){
}

public static SimpleSingleton getInstance(){
if (null == uniqueInstance){
// sleep this thread for 0.1 seconds, so that other thread may enter
// this point.
try{
Thread.sleep(100);
} catch (InterruptedException e){
e.printStackTrace();
}
uniqueInstance = new SimpleSingleton();
}
return uniqueInstance;

}

public static void main(String[] args){
for (int i = 0; i < 10; i++){
new Thread(new Runnable(){
@Override
public void run(){
SimpleSingleton a = SimpleSingleton.getInstance();
System.out.println("Newly created singleton = " + a);
}
}).start();
}
}

}

/**
* output:
*
* Newly created singleton = singleton.SimpleSingleton@5097878e Newly created
* singleton = singleton.SimpleSingleton@5d2a71b2 Newly created singleton =
* singleton.SimpleSingleton@585a62b3 Newly created singleton =
* singleton.SimpleSingleton@278d8d22 Newly created singleton =
* singleton.SimpleSingleton@63d6cae Newly created singleton =
* singleton.SimpleSingleton@31ed5890 Newly created singleton =
* singleton.SimpleSingleton@7f2d51f7 Newly created singleton =
* singleton.SimpleSingleton@22a0b4e0 Newly created singleton =
* singleton.SimpleSingleton@6c308163 Newly created singleton =
* singleton.SimpleSingleton@7595bd09
*/


2. synchronized Singleton:

为了解决上述代码多线程情况下的问题,使用synchronized把getInstance()方法变成同步方法。
但是,由此却也带来了性能下降的问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
* A synchronized singleton pattern.
* 此例子证明了synchronized单例模式能够保证多线程情况下依然能够正常工作
* 但是它依然会有负作用,同步会造成程序执行效率下降,因爲每次調用的時候都會進行同步。
* 因此如果这个类是频繁使用的,那么可能就需要重新考虑其他的方法了。
*
* @author jay
*
*/
public class SynchronizedSingleton{
// Singleton_1 has an instance : uniqueInstance
private static SynchronizedSingleton uniqueInstance;

// this constructor can only be invoked in its own class
private SynchronizedSingleton(){
}

/**
* @return
*/
public static synchronized SynchronizedSingleton getInstance(){
if (null == uniqueInstance){
uniqueInstance = new SynchronizedSingleton();
}
return uniqueInstance;
}
}


3. Double Checked Singleton

使用双重加锁,首先檢查是否已經有实例,如果沒有实例,再進行同步。只有第一次会同步,可以幫助提升性能.
注意volatile关键字的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class DoubleCheckedSingleton{
// Singleton_1 has an instance : uniqueInstance
private volatile static DoubleCheckedSingleton uniqueInstance;

// this constructor can only be invoked in its own class
private DoubleCheckedSingleton(){
}

/**
* @return
*/
public static DoubleCheckedSingleton getInstance(){
// 如果還沒有實例,就進行同步,創建一個,所以if框裏面的代碼只會執行一次
if (null == uniqueInstance){
synchronized (DoubleCheckedSingleton.class){
if (null == uniqueInstance){
/**
* 如果没有volatile,这里会有问题:
* 因为下面这一步实际上可以分为三步
* 1.分配内存 :allocat();
* 2. 实例化对象
* 3.将uniqueInstance指向分配的内存空间
*
* 如果没有volatile,这三步可能会因为指令重排序的关系,顺序变成1,3,2;
* 当线程A执行到3的时候,此时uniqueInstance!=null了;
* 这时候,线程B执行到外层的判断,uniqueIntance==null为false,这时候线程B直接返回尚未初始化的实例,
* 会造成程序崩溃。
**/
uniqueInstance = new DoubleCheckedSingleton();
}
}

}
// 如果已經有了實例就不需要再進行判斷了,直接返回實例即可
return uniqueInstance;
}
}


4. Eagerly Sinleton

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package build.singleton;

/**
* @author jay
*
*/
public class EagerlySinleton{
/****
* 如果應用程序總是創建或者使用singleton實例子,利用這個方法保證在JVM加載類的時候就馬上創建此唯一的實例
* JVM保證任何線程訪問之前一定先創建此實例
*/
private static EagerlySinleton uniqueInstance = new EagerlySinleton();

// this constructor can only be invoked in its own class
private EagerlySinleton(){
}

//已经有了实例,可以直接使用
public static EagerlySinleton getInstance(){
return uniqueInstance;
}
}

5. 反射破解

上述几个单例模式存在的共同问题是,使用反射或者序列化仍然可以产生多个不同的实例。
反射测试代码如下:

1
2
3
4
5
6
7
8
9
public static void main(String[] args) throws InstantiationException, IllegalAccessException{

Class<?> cl = DoubleCheckedSingleton.class;
Constructor<?> constructor = cl.getDeclaredConstructor(null);
constructor.setAccessible(true);
DoubleCheckedSingleton ins1 = (DoubleCheckedSingleton) cl.newInstance();
DoubleCheckedSingleton ins2 = (DoubleCheckedSingleton) cl.newInstance();
System.out.println("ins1==ins2 : "+(ins1 == ins2));
}


6. 枚举单例

枚举单例非常简单,并且是线程安全的,也可以防止单例模式被”攻击”。

1
2
3
4
5
6
7
public enum EnumSingleton{
INSTANCE;

public void doSomething(){
System.out.println("This is an EnumSingleton!");
}
}

测试代码如下:

1
2
3
4
5
6
7
8
9
10
11
public class TestEnumSingleton{

public static void main(String []ar) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
Class<?> cl = EnumSingleton.class;
Constructor<EnumSingleton> con = (Constructor<EnumSingleton>) cl.getDeclaredConstructor(null);
con.setAccessible(true);
EnumSingleton a = con.newInstance();
EnumSingleton b = con.newInstance();
System.out.println("a==b"+(a==b));
}
}

7. 静态内部类

静态内部类是利用JVM内部的锁机制来保证不会创建多个实例

1
2
3
4
5
6
7
8
9
public class SingletonFactory {
public static Singleton getInstance(){
return Holder.instance;
}

private static class Holder{
public static final Singleton instance = new Singleton();
}
}