单例模式 (Factory Pattern)
创建型模式
涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。
这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
优点:
内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例
避免对资源的多重占用(比如写文件操作)
缺点:
没有接口,不能继承,与单一职责原则冲突,
只关心内部逻辑,而不关心外面怎么样来实例化。
使用场景:
要求生产唯一序列号。
WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。
创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。
相关设计模式:
原型模式
单例模式是只有一个实例,原型模式每拷贝一次都会创造一个新的实例。
/**
* 双重校验锁
* JDK5 或更高版本使用
* 因为从 JDK5 开始使用新的 JSR-133 内存模型规范,这个规范增强了 volatile 的语义
* @author wangJiaLun
* @date 2019-04-15
**/
public class DoubleCheckLockSingleton {
// Volatile可以保证可见性和有序性,无法保证原子性
// 同时保证JVM对指令不会进行重排序
/**
* 需要添加 volatile 关键字原因是禁止重排序
* instance = new DoubleCheckLockSingleton();
* 分为三个指令(伪代码)
* memory = allocate(); // 1:分配对象的内存空间
* ctorInstance(memory); // 2:初始化对象
* instance = memory; // 3:设置 instance 指向刚分配的内存地址
* 《The Java Language Specification, Java SE 7 Edition》
* 规范中定义所有线程执行Java程序时必须遵守intra-thread semantics
* 保证重排序不会改变单线程内的程序执行结果,所以上述2,3步骤
* 允许被重排序提供程序执行效率
* 多线程并发时,如果重排序:
* instance对象已经被分配内存地址,未被初始化
* 其他线程会获取到一个未被初始化的instance
*/
private volatile static DoubleCheckLockSingleton instance;
private DoubleCheckLockSingleton(){}
public static DoubleCheckLockSingleton getInstance(){
// 判断对象是否已经生成
// 生成则不进入下面的同步代码
if (null == instance) {
synchronized (DoubleCheckLockSingleton.class){
// 当线程A通过第一层if判断准备进入同步代码时
// 线程B也通过判断进入同步代码
// 不加判断线程A&B都会创建新的对象
if (null == instance) {
instance = new DoubleCheckLockSingleton();
}
}
}
return instance;
}
}