1、定义
创建型模式,提供一种创建对象的方式。这种模式涉及到一个单一的类,该类负责创建自己的对象,并保证只有单个对象被创建,同时提供一个获取该对象的方法。
1.1、条件
- 单例只能有一个对象。
- 单例必须自己创建自己的唯一实例。
- 单例必须给所有其他对象提供这一实例使用。
2、详细介绍
- 意图:保证一个类仅有一个实例,并提供该类的全局使用方式。
- 解决的问题:公共的类提供全局使用方式,避免类的频繁创建和销毁。
- 使用场景:控制对象个数,节省资源消耗。
- 实现方式:其他对象调用该对象的公共方法时判断对象是否存在,不存在则创建,存在则返回。
- 关键代码:默认的构造函数必须私有化,禁止使用者new对象。
3、优缺点
3.1、优点
- 使用单例模式,内存中只有一个对象,减少内存开销。
- 避免对文件的重复占用,比如读写文件的时候,只用一个对象对文件进行读或者写。
3.2、缺点
- 没有接口不能继承,与单一职责冲突。一个类应该只关心内部逻辑,不应该来规范使用者怎么来实例化。
4、应用实例
- 产生唯一序列号,比如雪花算法生成主键。
- WEB中的页面计数器,不用每次刷新都在数据库里边记录一次,可以用单例对象先缓存起来。
- 创建一个全局独享,供系统使用,节约系统资源开销。比如:系统I/O和数据库链接等。
5、实现
5.1、懒汉式单例模式
5.1.1、详细介绍
- 简介:这种方式时Lazy初始化,就是说使用的时候才回去验证是否创建对象。最主要的是不支持多线程,并发时存在线程安全问题。
- 线程是否安全:否
- 是否lazy初始化:是
5.1.2、代码实现
/**
* Description: 懒汉式单例模式实现
* 是否lazy初始化: 是
* 是否线程安全: 否
* 说明: 这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程。因为没有加锁 synchronized,所以严格意义上它并不算单例模式。
* Created by Administrator
* Date 2020/1/27 17:18
*/
public class SlobSingle {
public static SlobSingle slobSingle;
// 默认构造函数私有化
private SlobSingle(){}
public static SlobSingle getInstance(){
if (null == slobSingle){
slobSingle = new SlobSingle();
}
return slobSingle;
}
}
5.2、优化后懒汉式单例模式
5.2.1、详细介绍
- 简介:在获取对象的方法上添加相乘同步锁
- 是都lazy初始化:是
- 是否线程安全:是
- 优点:第一次调用时才创建对象,避免内存浪费
- 缺点:调用方法加了同步锁,如果频繁使用,对系统性能是很严重的影响。
5.2.2、代码实现
/**
* Description: 优化懒汉式单例模式实现
* 是否lazy初始化: 是
* 是否线程安全: 是
* 缺点: 性能有消耗
* Created by Administrator
* Date 2020/1/27 17:18
*/
public class SlobSingleNew {
public static SlobSingleNew slobSingle;
// 默认构造函数私有化
private SlobSingleNew(){}
public static synchronized SlobSingleNew getInstance(){
if (null == slobSingle){
slobSingle = new SlobSingleNew();
}
return slobSingle;
}
}
5.3、饿汉式单例模式
5.3.1、详细介绍
- 简介:利用类加载机制,在装载类的时候就对对象进行了初始化。虽然这种方式可以避免线程安全问题,但是容易产生一些垃圾对象,应项系统资源消耗。
- 是否lazy初始化:否
- 是否线程安全:是
- 优点:获取对象的方法没有加锁,执行效率比较高,对性能影响小。
- 缺点:如果该对象不被使用,可能产生垃圾对象,增加系统资源消耗。
5.3.2、代码实现
/**
* Description: 饿汉式单例模式
* Created by Administrator
* Date 2020/1/27 17:34
*/
public class BadmashSingle {
private static BadmashSingle badmashSingle = new BadmashSingle();
private BadmashSingle(){}
public static BadmashSingle getInstance(){
return badmashSingle;
}
}
5.4、双检锁/双重校验锁(DCL,即 double-checked locking)
5.4.1、详细介绍
- 简介:这种方式采用双锁机制,安全且在多线程情况下能保持高性能。
- 是否 Lazy 初始化:是
- 是否多线程安全:是
5.4.2、代码实现
/**
* Description: 双检锁/双重校验锁(DCL,即 double-checked locking)
* Created by Administrator
* Date 2020/1/27 17:43
*/
public class DCLSingle {
private volatile static DCLSingle single;
private DCLSingle(){}
public static DCLSingle getInstance(){
if (null == single){
synchronized (DCLSingle.class){
if (null == single){
single = new DCLSingle();
}
}
}
return single;
}
}
5.5、登记式/静态内部类
5.5.1、详细介绍
- 简介:使用静态内部类在类加载的时候将对象的初始化完成,同样利用了类加载器的机制。
- 是否 Lazy 初始化:是
- 是否多线程安全:是
5.5.2、代码实现
/**
* Description: 登记式/静态内部类 单例模式
* Created by Administrator
* Date 2020/1/27 17:50
*/
public class StaticClassSingle {
private static class StaticClassSingleHolder{
private static final StaticClassSingle single = new StaticClassSingle();
}
private StaticClassSingle(){}
public static final StaticClassSingle getInstance(){
return StaticClassSingleHolder.single;
}
}
5.6、枚举式单例模式
5.6.1、详细介绍
- 简介:应用较少,实现简介,自动支持序列化机制,绝对防止多次实例化。是目前最好的单例模式实现方式。
- JDK 版本:JDK1.5 起
- 是否 Lazy 初始化:否
- 是否多线程安全:是
- 描述:这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。不过,由于 JDK1.5 之后才加入 enum 特性,用这种方式写不免让人感觉生疏,在实际工作中,也很少用。
- 缺点:不能通过reflection attack 来调用私有构造方法。
5.6.2、代码实现
/**
* Description: 枚举式单例模式
* Created by Administrator
* Date 2020/1/27 17:58
*/
public enum EnumSingle {
INSTANCE;
public void method() {
}
}
5.7、几种设计模式使用场景
一般情况下,不建议使用第 1 种和第 2 种懒汉方式,建议使用第 3 种饿汉方式。只有在要明确实现 lazy loading 效果时,才会使用第 5 种登记方式。如果涉及到反序列化创建对象时,可以尝试使用第 6 种枚举方式。如果有其他特殊的需求,可以考虑使用第 4 种双检锁方式。