What is Singleton pattern?
In Wikipedia, there is an explanation:"In software engineering, the singleton pattern is a design pattern that restricts the instantiation of a class to one object."
一、什么是单例模式?
在维基百科中,是这样解释的,“在软件工程中,单例模式指的是对类加以限制,只允许创建一个对象的设计模式”。
也就是说,在整个应用程序的生命周期中,任何一个时刻,单例类的实例都只存在一个(当然也可以不存在)。
单例模式是一种对象创建型设计模式,在《设计模式:可复用面向对象软件的基础》一书中提到,单例模式:“保证一个类仅有一个实例,并提供一个访问它的全局访问点”。
让类自身负责保存它的唯一实例,这个类可以保证没有其他实例被创建(通过截取创建新对象的请求)。
二、我们为什么要使用单例模式?
单例模式应该是设计模式中最简单的一种设计模式,它的应用场景如下:
在工作过程中,有些对象我们只需要一个,比如线程池、缓存、硬件设备等,
如果有多个实例同时使用,可能会造成执行冲突、结果不一致等问题。
比如我们创建了多个打印程序实例,或打印机对象,但实际的打印设备只有一台,或者打印假脱机只有一个,程序执行时,就可能会造成打印结果的混乱并使程序失去可再现性。
再比如,在一个父容器中点击某个菜单项打开一个子窗口,如果不加以控制的话,每次单击菜单项都会打开一个新窗口。这不仅会浪费内存资源,在程序逻辑上也是不可以接受的。
那么我们该如何解决这个问题呢?这就要用到下面所要详细介绍的单例模式。
三、经典单例模式
1.经典单例模式的UML图如下:
2.代码实现如下:
1 public class Singleton {
2
3 // 静态的instance对象,保证全局唯一性
4 private static Singleton instance = null;
5
6 // 私有的构造函数,防止外部用new关键字创建实例对象
7 private Singleton() {
8
9 }
10
11 // 对外的公共静态实例方法,从类级别直接可以调用此方法
12 public static Singleton getInstance() {
13
14 // 通过判断instance是否为null,决定是否创建对象
15 if (instance == null) {
16 instance = new Singleton();
17 }
18 return instance;
19 }
20 }
注意:通过Java反射机制是能够实例化构造方法为private的类的,此时基本上所有的Java单例实现失效。
(事实上我们一般不需要这样做,所以单例模式仍有其存在的意义)
四、单例模式在多线程环境下存在的问题
对于上述代码,我们可以考虑这样一种情况:
当有两个Singleton类的实例同时被创建,并运行于不同的线程中。假如A线程在执行完上述代码第15行后意外阻塞,而B线程将继续运行,执行第16行后创建instance实例,此后如果A线程重新回到就绪状态,并得到处理器资源,进入运行状态,将再次创建instance对象,此时单例模式失效。为了解决这一问题,程序员们对单例模式进行了优化。
五、单例模式的分类
1.饿汉式单例
急切创建实例,在类初始化时,便自行实例化。
1 public class Singleton1 {
2
3 //已经实例化
4 private static final Singleton1 single = new Singleton1();
5
6 //私有的默认构造方法
7 private Singleton1() {
8
9 }
10
11 //静态工厂方法
12 public static Singleton1 getInstance() {
13 return single;
14 }
15 }
2.懒汉式单例
在第一次调用的时候再实例化。
1 public class Singleton2 {
2
3 //注意,这里没有final
4 private static Singleton2 single = null;
5
6 //私有的默认构造子
7 private Singleton2() {
8
9 }
10
11 //静态工厂方法,用synchronized加锁
12 public synchronized static Singleton2 getInstance() {
13 if (single == null) {
14 single = new Singleton2();
15 }
16 return single;
17 }
18 }
这种方式也存在一定的问题,同样考虑特殊情况,当A线程执行到getInstance内时意外出现阻塞,此时B线程中的实例也不能够执行getInstance操作,出现阻塞。
为了解决这一问题,对此方法可以做进一步优化:双重检查加锁法。
虽然此方法同样不完美,但相对于上面一种情形,已经做到了一定的优化。
1 public class Singleton3 {
2
3 // 添加关键词volatile
4 private volatile static Singleton3 single = null;
5
6 //私有的默认构造器
7 private Singleton3() {
8
9 }
10
11 public static Singleton3 getInstance() {
12 if (single == null) {
13 //同步锁
14 synchronized (Singleton3.class) {
15 if (single == null) {
16 single = new Singleton3();
17 }
18 }
19 }
20 return single;
21 }
22 }
3.登记式单例
将类名注册,下次从里面直接获取。
1 import java.util.HashMap;
2 import java.util.Map;
3
4 public class Singleton4 {
5 private static Map<String,Singleton4> map = new HashMap<String,Singleton4>();
6
7 static{
8 Singleton4 single = new Singleton4();
9 map.put(single.getClass().getName(), single);
10 }
11 //保护的默认构造器
12 protected Singleton4(){
13
14 }
15
16 //静态工厂方法,返还此类惟一的实例
17 public static Singleton4 getInstance(String name) {
18 if(name == null) {
19 name = Singleton3.class.getName();
20 }
21 if(map.get(name) == null) {
22 try {
23 map.put(name, (Singleton4) Class.forName(name).newInstance());
24 } catch (InstantiationException e) {
25 e.printStackTrace();
26 } catch (IllegalAccessException e) {
27 e.printStackTrace();
28 } catch (ClassNotFoundException e) {
29 e.printStackTrace();
30 }
31 }
32 return map.get(name);
33 }
34 }
参考资料:
1.极客学院hexter老师的课程—— 设计模式之单例模式
2.《设计模式:可复用面向对象软件的基础》
Erich Gamma,Richard Helm,Ralph Johnson著
3.博文:蛊惑Into—— Java单例模式详解
4.维基百科:Singleton Pattern
推荐阅读:
博文:赵学智@行胜于言—— 设计模式培训之一:为什么要用单例模式?
博文:都市耕牛—— 登记式单例实现单例模式的继承(限定一个抽象类的所有子类都必须是单例)
本文详细介绍了单例模式的概念、应用场景及其实现方式。探讨了单例模式如何确保一个类只有一个实例,并提供了全局访问点。此外,还分析了单例模式在多线程环境下面临的问题及其解决方案。
970

被折叠的 条评论
为什么被折叠?



