什么是单例模式?
指一个类只有一个实例,且该类能自行创建这个实例的一种模式。例如,Windows 中只能打开一个任务管理器,这样可以避免因打开多个任务管理器窗口而造成内存资源的浪费,或出现各个窗口显示内容的不一致等错误。
在计算机系统中,还有 Windows 的回收站、操作系统中的文件系统、多线程中的线程池、显卡的驱动程序对象、打印机的后台处理服务、应用程序的日志对象、数据库的连接池、网站的计数器、Web 应用的配置对象、应用程序中的对话框、系统中的缓存等常常被设计成单例。
单例模式的实现方式
饿汉模式(线程安全)
饿汉式在类初始化的时候就创建了对象,加载到了内存。因为对象已经创建好了,饿汉式是可以保证线程安全的。
但是在没有使用这个对象的情况下就加载到内存是一种很大的浪费。
要想让一个类只能构建一个对象,自然不能让它随便去做new操作,因此Signleton的构造方法是私有的
public class Singleton {
private Singleton() {} //私有构造函数
private static Singleton instance = null; //单例对象
//静态工厂方法
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
懒汉模式(最优)即 线程安全的双重检查锁单例
public class Singleton {
private static volatile Singleton instance = null;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
1.为了防止new Singleton被执行多次,因此在new操作之前加上Synchronized 同步锁,锁住整个类。
2.第一次判断 if (instance ==null) 的原因:在第一次之后,instance已经不再为null,直接执行return语句
3.第二次判断if (instance == null) 的原因:当一个线程在创建一个对象后,由于其他线程已经在第一轮判断时就进入了方法,所以其他线程会继续走synchronized代码块,这时进行第二轮判断,其他线程会在第二轮判断时发现对象已经创建,则不会再继续创建对象。
4.使用volatile 的原因:
java中简单的一句 instance = new Singleton,会被编译器编译成如下JVM指令:
memory =allocate(); //1:分配对象的内存空间
ctorInstance(memory); //2:初始化对象
instance =memory; //3:设置instance指向刚分配的内存地址
但是这些指令顺序并非一成不变,有可能会经过JVM和CPU的优化,指令重排成下面的顺序:
memory =allocate(); //1:分配对象的内存空间
instance =memory; //3:设置instance指向刚分配的内存地址
ctorInstance(memory); //2:初始化对象
如果当线程A执行完1,3,时,instance对象还未完成初始化,但已经不再指向null。此时如果线程B抢占到CPU资源,执行 if(instance == null)的结果会是false,从而返回一个没有初始化完成的instance对象。
经过volatile的修饰,当线程A执行instance = new Singleton的时候,JVM执行顺序始终保证是下面的顺序:
memory =allocate(); //1:分配对象的内存空间
ctorInstance(memory); //2:初始化对象
instance =memory; //3:设置instance指向刚分配的内存地址
可以避免返回一个没有初始化完成的instance对象。