前言:
小伙伴们,大家好,我是狂奔の蜗牛rz,当然你们可以叫我蜗牛君,我是一个学习Java一年多时间的小菜鸟,同时还有一个伟大的梦想,那就是有朝一日,成为一个优秀的Java架构师。这个设计模式学习系列是用来记录我学习设计模式基础知识的全过程 ,废话不多说,开始今天关于单例模式学习吧!
单例模式(Singleton)是广泛使用的设计模式,主要作用是保证Java程序中某个类只有一个实例存在。
特点:
- 避免实例对象重复创建,减少每次创建对象时间开销,节约内存空间;
- 避免由于操作对个实例导致逻辑错误。
应用场景:一些管理器和控制器被设计为单例模式 。
1.饿汉模式
1.1 概念基础
特点: 饿汉模式在类加载时就对实例进行创建, 实例在整个程序周期都存在。
优点: 类加载时创建一次实例, 不会出现多个线程创建多个实例, 避免多线程同步问题。
缺点: 即使单例未被使用也会在类加载之后被创建, 浪费内存空间 。
适用场景: 单例占用内存小, 初始化时就会被使用。
1.2 代码实现
/**
* @ClassName CodeMapping
* @Description 代码转化类
* @author 狂奔de蜗牛rz
* @date 2022/06/06
*/
public class CodeMapping {
// 创建私有静态变量obj(即CodeMapping类的对象实例)
private static CodeMapping obj = new CodeMapping();
// CodeMapping(代码转化类)的无参构造函数
private CodeMapping() {};
/**
* @Description 获取CodeMapping的单例对象(使用饿汉模式)
* @return CodeMapping 代码转化类
*/
public static CodeMapping getInstance(){
// 将obj(代码转化对象实例)进行返回
return obj;
}
}
2.懒汉模式
2.1 概念基础
特点:使用懒汉模式,可以实现延迟加载
优点:懒汉模式中单例在需要时才创建,若单例已创建,再次调用不会重新创建,而是直接返回之前创建的对象
缺点:未考虑线程安全问题,若多个线程并发调用getInstance方法,则会导致创建多个实例
适用场景:若使用某个单例次数少,且创建消耗资源多,需实现单例按需创建
2.2 懒汉式代码实现
/**
* @ClassName CodeMapping
* @Description 代码转化类
* @author 狂奔de蜗牛rz
* @date 2022/06/06
*/
public class CodeMapping {
// 创建私有静态变量obj(即CodeMapping类的对象实例), 其初值为null
private static CodeMapping obj = null;
// CodeMapping(代码转化类)的无参构造函数
private CodeMapping() {};
/**
* @Description 获取CodeMapping的单例对象(使用懒汉模式)
* @return CodeMapping 代码转化类
*/
public static CodeMapping getInstance(){
// 判断obj(代码转化对象实例)是否为空
if(obj == null) {
// 若为空, 则重新创建CodeMapping(代码生成类)对象实例
obj = new CodeMapping();
}
// 将obj(代码转化对象实例)进行返回
return obj;
}
}
2.3 使用同步锁解决线程安全问题
2.3.1 概念基础
懒汉模式加上synchronized同步锁, 可以实现延迟加载, 并且解决线程并发问题。
2.3.2 代码实现
/**
* @ClassName CodeMapping
* @Description 代码转化类
* @author 狂奔de蜗牛rz
* @date 2022/06/06
*/
public class CodeMapping {
// 创建私有静态变量obj(即CodeMapping类的对象实例), 其初值为null
private static CodeMapping obj = null;
// CodeMapping(代码转化类)的无参构造函数
private CodeMapping() {};
/**
* @Description 获取CodeMapping的单例对象(使用懒汉模式)
* @return CodeMapping 代码转化类
*/
public static synchronized CodeMapping getInstance(){
// 判断obj(代码转化对象实例)是否为空
if(obj == null) {
// 若为空, 则重新创建CodeMapping(代码生成类)对象实例
obj = new CodeMapping();
}
// 将obj(代码转化对象实例)进行返回
return obj;
}
}
3.双重校验锁
3.1 概念基础
优点:不仅实现延迟加载,还解决了线程并发问题, 同时提高执行效率。
缺点:双重校验锁虽然可以解决线程并发问题, 但存在指令重排优化问题。
3.2 双重校验锁代码实现
/**
* @ClassName CodeMapping
* @Description 代码转化类
* @author 狂奔de蜗牛rz
* @date 2022/06/06
*/
public class CodeMapping {
/* 单例模式(双重校验锁), 实现延迟加载, 解决线程并发问题, 解决提高执行效率 */
// 创建私有静态变量obj(即CodeMapping类的对象实例), 其初值为null
private static CodeMapping obj = null;
// CodeMapping(代码转化类)的无参构造函数
private CodeMapping() {};
/**
* @Description 获取CodeMapping的单例对象(使用双重校验锁),
* 大部分情况下调用getInstance, 都有不会执行到同步代码块,
* @return CodeMapping 代码转化类
*/
public static CodeMapping getInstance(){
// 第一次判断obj(代码转化对象实例)是否为空
if(obj == null) {
// 给CodeMapping类加同步锁
synchronized (CodeMapping.class) {
// 第二次判断obj(代码转化对象实例)是否为空
if(obj == null) {
// 若为空, 则重新创建CodeMapping(代码生成类)对象实例
obj = new CodeMapping();
}
}
}
// 将obj(代码转化对象实例)进行返回
return obj;
}
}
3.3 使用volatile关键字解决指令重排优化
3.3.1 概念基础
优点:使用volatile关键字, 可以禁止指令重排序优化(即保证单例对象在被赋值时已被初始化)。
3.3.2 代码实现
/**
* @ClassName CodeMapping
* @Description 代码转化类
* @author 狂奔de蜗牛rz
* @date 2022/06/03
*/
public class CodeMapping {
// 创建私有静态变量obj(即CodeMapping类的对象实例), 使用volatile关键字 ,其初值为null
private static volatile CodeMapping obj = null;
// CodeMapping(代码转化类)的无参构造函数
private CodeMapping() {};
/**
* @Description 获取CodeMapping的单例对象(使用双重校验锁),
* 大部分情况下调用getInstance, 都有不会执行到同步代码块,
* @return CodeMapping 代码转化类
*/
public static CodeMapping getInstance(){
// 第一次判断obj(代码转化对象实例)是否为空
if(obj == null) {
// 给CodeMapping类加同步锁
synchronized (CodeMapping.class) {
// 第二次判断obj(代码转化对象实例)是否为空
if(obj == null) {
// 若为空, 则重新创建CodeMapping(代码生成类)对象实例
obj = new CodeMapping();
}
}
}
// 将obj(代码转化对象实例)进行返回
return obj;
}
}
好了,今天的有关 单例模式 的学习就到此结束啦。欢迎小伙伴们积极学习和讨论,喜欢的可以给蜗牛君点个关注,顺便来个一键三连。我们下期见,拜拜啦!
参考博客链接:https://blog.csdn.net/goodlixueyong/article/details/51935526