详解设计模式-单例模式
简介
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
设计模式分为三大类:
创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
注意:
- 单例类只能有一个实例。
- 单例类必须自己创建自己的唯一实例。
- 单例类必须给所有其他对象提供这一实例。
实现细节
意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
主要解决:一个全局使用的类频繁地创建与销毁。
何时使用:当您想控制实例数目,节省系统资源的时候。
如何解决:判断系统是否已经有这个单例,如果有则返回,如果没有则创建。
关键代码:构造函数是私有的。
优点:
- 在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例。
- 避免对资源的多重占用(比如写文件操作)。
缺点:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
实现方式
懒汉式 < Lazy singleton >
懒初始化:否
多线程安全:是 (通过synchronized关键字 + 双重校验锁 )
描述: 通过双锁机制,安全且在多线程情况下能保持高性能。getInstance() 的性能对应用程序很关键。
在synchronized关键字内外都加了一层 if 条件判断,这样既保证了线程安全,又比直接上锁提高了执行效率,还节省了内存空间
package com.study.designmode.singleton.lazy;
/**
* Lazy singleton
*
* @author yongwang.lu
*/
public class LazySingleton {
/**
* 实例
* volatile 修饰
*/
private volatile static LazySingleton instance;
/**
* 私有化构造函数
*/
private LazySingleton() {
}
/**
* 获取实例入口
*
* @return LazySingleton
*/
public static LazySingleton getInstance() {
if (instance == null) {
// 双重校验锁
synchronized (LazySingleton.class) {
// 防止多线程多次初始化
if (instance == null) {
instance = new LazySingleton();
}
}
}
return instance;
}
}
饿汉式< Hungry singleton >
懒初始化:否
多线程安全:是 (Java类加载机制避免了多线程的同步问题)
描述:这种方式比较常用,但容易产生垃圾对象。
- 优点:没有加锁,执行效率会提高。
- 缺点:类加载时就初始化,浪费内存。
package com.study.designmode.singleton.hungry;
/**
* Lazy singleton
*
* @author yongwang.lu
*/
public class HungrySingleton {
/**
* 实例
*/
private static final HungrySingleton INSTANCE = new HungrySingleton();
/**
* 私有化构造函数
*/
private HungrySingleton() {
// 防止反射攻击
if (INSTANCE != null) {
throw new RuntimeException("单例不允许多个实例");
}
}
/**
* 获取实例入口
*
* @return LazySingleton
*/
public static HungrySingleton getInstance() {
return INSTANCE;
}
}
静态内部类< Inner class singleton >
懒初始化:是
多线程安全:是 (Java类加载机制避免了多线程的同步问题)
描述:个人理解这个是对饿汉式的优化
饿汉式方式只要类被装载了,那么 instance 就会被实例化(没有达到懒初始化效果),而这种方式是 Singleton 类被装载了,instance 不一定被初始化。因为 SingletonHolder 类没有被主动使用,只有通过显式调用 getInstance 方法时,才会显式装载 SingletonHolder 类,从而实例化 instance。
package com.study.designmode.singleton.inner;
/**
* Inner class singleton
*
* @author yongwang.lu
*/
public class InnerClassSingleton {
/**
* 静态内部类
*/
private static class InnerClassSingletonHolder {
private static final InnerClassSingleton INSTANCE = new InnerClassSingleton();
}
/**
* 私有化构造函数
*/
private InnerClassSingleton() {
// 防止反射攻击
if (InnerClassSingletonHolder.INSTANCE != null) {
throw new RuntimeException("单例不允许多个实例");
}
}
public static InnerClassSingleton getInstance() {
return InnerClassSingletonHolder.INSTANCE;
}
}
枚举< Enum singleton >
懒初始化:否
多线程安全:是 (Java类加载机制避免了多线程的同步问题)
描述:这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。
这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。不过,由于 JDK1.5 之后才加入 enum 特性,用这种方式写不免让人感觉生疏,在实际工作中,也很少用。
不能通过 reflection attack 来调用私有构造方法。
package com.study.designmode.singleton.enums;
/**
* EnumSingleton
*
* @author yongwang.lu
*/
public enum EnumSingleton {
/**
* 实例
*/
INSTANCE;
EnumSingleton() {
}
}