主要是从 Head Fisrt 设计模式中学习到知识;
1. 定义单件模式
单件模式确保一个类只有一个实例,并提供一个全局访问点;
在整个系统上下文中,只有一个对象,对于很多在系统中只需要一个或者创建代价比较大的对象,可以使用,例如:线程池、缓存、对话框、处理偏好设置和注册表对象、日志对象、充当打印机、显卡等设备的驱动程序的对象;
采用单件模式可以避免系统维护太多没有记录状态数据、所有实例功能可替代的相关对象,还有就是多个实例处理会可能会造成结果不一致的问题;
2. 主要思想
2.1 由类持有一个静态的类对象,并通过方法暴露对类对象的获取接口;
2.2 类禁止外部对象实例化
3. 单件模式的实现
3.1 下面是单件模式的简单实现,思想就是持有一个静态对象,当需要的时候就通过类的公用方法获取对象的引用,这里还有一个做法就是延迟实例化,当对象需要用到的时候才会去实例化对象,如果对象没有被引用到,那么就是一直都不会实例化,系统资源也不会浪费。
public class Singleton { private static Singleton uniqueInstance; private Singleton () {} public static Singleton getInstance () { if (uniqueInstance == null) { uniqueInstance = new Singleton(); } } return uniqueInstance; } public static void main(String[] args) { Singleton singleton = Singleton.getInstance(); System.out.println(singleton.toString()); // [email protected] singleton = Singleton.getInstance(); System.out.println(singleton.toString()); // [email protected] singleton = Singleton.getInstance(); System.out.println(singleton.toString()); // [email protected] singleton = Singleton.getInstance(); System.out.println(singleton.toString()); // [email protected] } }
这里打印出来,几个类的对象的内存地址都是同一个,引用了同一个对象。
4. 单件模式和多线程
上面的单件模式实现已经能够满足基本的使用要求,但是当单件模式模式遇到多线程之后,很多奇怪的问题就发生了(事实上很多代码遇到多线程后都会有问题),
例如上面的例子,当代码的推进状态像下面的状态,就会出现问题:
这个时候对象已经不是同一个了,多线程造成 单件模式 已经和我们定义的不一样了。
为了解决这个问题,有以下的三个解决方法:
* 使用synchronized方法,将getInstance()变成同步的方法,这样能够解决我们的问题,但是实例化只是在第一次的时候使用,后面就没有这个问题存在了,然后通过synchronized强制同步会降低系统的并发性能,这种情况适合getInstance()的性能不影响或者影响了可以接受的地方;
public static synchronized Singleton getInstance () { ...
* 使用“急切”的实例化方法,在类初始化的时候就像对象生成;
public class Singleton { private static Singleton uniqueInstance = new Singleton(); private Singleton () {} public static Singleton getInstance () { return uniqueInstance; } }
* 使用“双重检查加锁”( since jdk 1.4 )
使用之后代码是这样子的:
public class Singleton { private volatile static Singleton uniqueInstance; private Singleton () {} public static Singleton getInstance () { if (uniqueInstance == null) { synchronized (Singleton.class) { if (uniqueInstance == null){ uniqueInstance = new Singleton(); } } } return uniqueInstance; } public static void main(String[] args) { System.out.println("test"); Singleton singleton = Singleton.getInstance(); System.out.println(singleton.toString()); singleton = Singleton.getInstance(); System.out.println(singleton.toString()); singleton = Singleton.getInstance(); System.out.println(singleton.toString()); singleton = Singleton.getInstance(); System.out.println(singleton.toString()); } }
通过 volatile 标识该变量的修改是对其他线程可见的,还有禁止指令排序;详细用法见:地址
然后当判断是未初始化的时候,再将类进入锁定然后再次判空之后再初始化类,这样就解决了我们前面遇到的问题,也很好的实现了单件模式的理念。
原文地址:https://www.cnblogs.com/zhuangmingnan/p/9383859.html