单例模式
1. 单例模式介绍
1.1 定义
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一,此模式保证某个类在运行期间,只有一个实例对外提供服务,而这个类被称为单例类。
单例模式也比较好理解,比如一个人一生当中只能有一个真实的身份证号,一个国家只有一个政府,类似的场景都是属于单例模式。
1.2 使用单例模式要做的两件事
- 保证一个类只有一个实例
- 为该实例提供一个全局访问节点
1.3 单例模式结构

2.单例模式的实现
2.1 饿汉式
在类加载期间初始化静态实例,保证 instance 实例的创建是线程安全的 ( 实例在类加载时实例化,有JVM保证线程安全).
特点: 不支持延迟加载实例(懒加载) , 此中方式类加载比较慢,但是获取实例对象比较快
问题: 该对象足够大的话,而一直没有使用就会造成内存的浪费。
public class Singleton_01 {
//1. 私有构造方法
private Singleton_01(){
}
//2. 在本类中创建私有静态的全局对象
private static Singleton_01 instance = new Singleton_01();
//3. 提供一个全局访问点,供外部获取单例对象
public static Singleton_01 getInstance(){
return instance;
}
}
2.3 懒汉式(线程不安全)
此种方式的单例实现了懒加载,只有调用getInstance方法时 才创建对象.但是如果是多线程情况,会出现线程安全问题.
public class Singleton_02 {
//1. 私有构造方法
private Singleton_02(){
}
//2. 在本类中创建私有静态的全局对象
private static Singleton_02 instance;
//3. 通过判断对象是否被初始化,来选择是否创建对象
public static Singleton_02 getInstance(){
if(instance == null){
instance = new Singleton_02();
}
return instance;
}
}
假设在单例类被实例化之前,有两个线程同时在获取单例对象,线程A在执行完if (instance == null) 后,线程调度机制将 CPU 资源分配给线程B,此时线程B在执行 if (instance == null)时也发现单例类还没有被实例化,这样就会导致单例类被实例化两次。为了防止这种情况发生,需要对 getInstance() 方法同步处理。改进后的懒汉模式.
2.4 懒汉式(线程安全)
原理: 使用同步锁 synchronized锁住 创建单例的方法 ,防止多个线程同时调用,从而避免造成单例被多次创建
-
即,
getInstance()方法块只能运行在1个线程中 -
若该段代码已在1个线程中运行,另外1个线程试图运行该块代码,则 会被阻塞而一直等待
-
而在这个线程安全的方法里我们实现了单例的创建,保证了多线程模式下 单例对象的唯一性
public class Singleton_03 {
//1. 私有构造方法
private Singleton_03(){
}
//2. 在本类中创建私有静态的全局对象
private static Singleton_03 instance;
//3. 通过添加synchronize,保证多线程模式下的单例对象的唯一性
public static synchronized Singleton_03 getInstance(){
if(instance == null){
instance = new Singleton_03();
}
return instance;
}
}
懒汉式的缺点也很明显,我们给 getInstance() 这个方法加了一把大锁(synchronzed),导致这个函数的并发度很低。量化一下的话,并发度是 1,也就相当于串行操作了。而这个函数是在单例使用期间,一直会被调用。如果这个单例类偶尔会被用到,那这种实现方式还可以接受。但是,如果频繁地用到,那频繁加锁、释放锁及并发度低等问题,会导致性能瓶颈,这种实现方式就不可取了。
2.5 双重校验
饿汉式不支持延迟加载,懒汉式有性能问题,不支持高并发。那我们再来看一种既支持延迟加载、又支持高并发的单例实现方式,也就是双重检测实现方式。
实现步骤:
- 在声明变量时使用了 volatile 关键字,其作用有两个:
保证变量的可见性:当一个被volatile关键字修饰的变量被一个线程修改的时候,其他线程可以立刻得到修改之后的结果。
屏蔽指令重排序:指令重排序是编译器和处理器为了高效对程序进行优化的手段,它只能保证程序执行的结果时正确的,但 是无法保证程序的操作顺序与代码顺序一致。这在单线程中不会构成问题,但是在多线程中就会出现问题。
- 将同步方法改为同步代码块. 在同步代码块中使用二次检查,以保证其不被重复实例化 同时在调用getInstance()方法时不进行同步锁,效率高。
/**
* 单例模式-双重校验
* @author spikeCong
* @date 2022/9/5
**/
public class Singleton_04 {
//使用 volatile保证变量的可见性
private volatile static Singleton_04 instance = null;
private Singleton_04()
单例模式:定义、实现与总结

最低0.47元/天 解锁文章
943

被折叠的 条评论
为什么被折叠?



