Fork me on GitHub

单例模式

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

public static SingleTon getInstance(){
return new SingleTon();
}
}
  • 1.懒汉式单例模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@NotThreadSafe
public class SingletonExample1 {
//私有构造函数
private SingletonExample1(){
}
//单例对象
private static SingletonExample1 instance=null;
//静态工厂方法
public static SingletonExample1 getInstance(){
if(instance==null){
instance=new SingletonExample1();
}
return instance;
}
}
  • 2.饿汉式单例模式

1
2
3
4
5
6
7
8
9
10
11
12
@ThreadSafe
public class SingleTonExample2 {
//私有构造函数
private SingleTonExample2(){
}
//单例对象
private static SingleTonExample2 singleTonExample2=new SingleTonExample2();
//静态工厂方法
public static SingleTonExample2 getInstance() {
return singleTonExample2;
}
}

上述两种单例模式中,饿汉式单例模式是线程安全的,而懒汉式单例模式则不然。可以通常下述两种方式让懒汉式单例模式变得安全:
一.使用synchronized来处理。也就是说将getInstance()方法变成同步方法即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@ThreadSafe
@NotRecommend
public class SingleTonExample3 {
//私有构造函数
private SingleTonExample3(){
}
//单例对象
public static SingleTonExample3 singleTonExample3=null;
//静态工厂方法
public static synchronized SingleTonExample3 getSingleTonExample3(){
if(singleTonExample3==null){
singleTonExample3=new SingleTonExample3();
}
return singleTonExample3;
}
}

二、 用“双重检查加锁”,在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
28
@NotThreadSafe
public class SingletonExample4 {
//私有构造函数
private SingletonExample4(){
}

//1.memory=allocate() 分配对象的内存空间
//2.ctorInstance()初始化对象
//3.instance=memory 设置instance指向刚分配的内存

//jvm和cpu优化,发生了指令重排
//1.memory=allocate() 分配对象的内存空间
//3.instance=memory 设置instance指向刚分配的内存
//2.ctorInstance()初始化对象
//单例对象
private static SingletonExample4 instance=null;
//静态工厂方法
public static SingletonExample4 getInstance(){
if(instance==null){//双重锁检查机制
synchronized (SingletonExample4.class){//同步锁
if(instance==null){
instance=new SingletonExample4();
}
}
}
return instance;
}
}

在注释中也讲了,因为指令重排的可能性,这种方式并不能保证线程安全。
解决的办法也很简单,使用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
@ThreadSafe
public class SingletonExample5 {
//私有构造函数
private SingletonExample5(){
}

//1.memory=allocate() 分配对象的内存空间
//2.ctorInstance()初始化对象
//3.instance=memory 设置instance指向刚分配的内存

//jvm和cpu优化,发生了指令重排
//1.memory=allocate() 分配对象的内存空间
//3.instance=memory 设置instance指向刚分配的内存
//2.ctorInstance()初始化对象
//单例对象
private static volatile SingletonExample5 instance=null;//volatile+双重检测->不允许指令重排
//静态工厂方法
public static SingletonExample5 getInstance(){
if(instance==null){//双重锁检查机制
synchronized (SingletonExample5.class){//同步锁
if(instance==null){
instance=new SingletonExample5();
}
}
}
return instance;
}
}

3.枚举方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@ThreadSafe
@Recommend
public class SingleTonExample7 {
private SingleTonExample7(){
}
public static SingleTonExample7 getInstance(){
return SingleTon.INSTANCE.getInstance();
}
private enum SingleTon{
INSTANCE;

private SingleTonExample7 singleTon;
//JVM保证这个方法绝对只调用一次
SingleTon(){
singleTon=new SingleTonExample7();
}

public SingleTonExample7 getInstance(){
return singleTon;
}
}
}

这是推荐的也是最安全的使用单例模式的方法,原因写在注释中,因为JVM保证SingleTon()方法绝对只调用一次,我们不需要添加任何额外的措施就可以保证安全。

单例模式优缺点:

优点:
一、节约了系统资源。由于系统中只存在一个实例对象,对与一些需要频繁创建和销毁对象的系统而言,单
例模式无疑节约了系统资源和提高了系统的性能。
二、因为单例类封装了它的唯一实例,所以它可以严格控制客户怎样以及何时访问它。

缺点:
一、由于单例模式中没有抽象层,因此单例类的扩展有很大的困难。
二、单例类的职责过重,在一定程度上违背了“单一职责原则”。

钱聪 wechat
欢迎交流学习。
么么!