设计模式-单例模式
这次主要记录下单例模式的学习过程
一、什么是单例模式
- 单例模式(Singleton Pattern):保证一个类仅有一个实例,并提供一个访问它的全局访问点
- 可划分为 饿汉式 和 懒汉式 两种
- 特点:
- 单例类确保自己只有一个实例
- 单例类必须自己创建自己的实例
- 单例类必须为其他对象提供唯一的实例
- 优点:
- 在内存里只有一个实例,减少了内存的开销
- 避免了对于资源的多重占用 (如读写操作)
- 由于类控制了实例化过程,所以类可以灵活地更改实例化过程
- 缺点:
- 由于不是抽象的,所以扩展性较差
- 职责过重,在一定程度上,违背了单一职责原则
二、单例模式—饿汉式
- 饿汉式,顾名思义,该单例类十分饿,因此需要在类一加载的时候就创建实例,是 线程安全 的
- 不管使不使用,只要类一加载就会创建实例,因此如果不使用的话,会浪费内存空间,但在需要用的时候,可以直接返回已经创建好的实例,节省了时间消耗
- 是典型的 空间换时间 的示例
饿汉式类
/**
* @Title: HungrySingletonPattern.java
* @Description: 饿汉式类
* @Author: xj
* @Date: 2018/10/20 10:04
*/
public class HungrySingletonPattern {
private static HungrySingletonPattern hungrySingletonPattern = new HungrySingletonPattern();
// 私有化构造函数,防止通过new的方式来创建实例
private HungrySingletonPattern() {
System.out.println("饿汉式 ---> 调用构造方法");
}
// 提供一个公共的,静态的,返回本类实例的方法
public static HungrySingletonPattern getInstance() {
System.out.println("饿汉式 ---> 调用静态方法返回实例");
return hungrySingletonPattern;
}
}
测试类
/**
* @Title: Test.java
* @Description: 测试类
* @Author: xj
* @Date: 2018/10/20 10:16
*/
public class Test {
public static void main(String[] args) {
// 饿汉式
System.out.println("=============== 单例模式: 饿汉式 ===============");
HungrySingletonPattern h1 = HungrySingletonPattern.getInstance();
HungrySingletonPattern h2 = HungrySingletonPattern.getInstance();
if (h1 == h2) {
System.out.println("=============== h1与h2为同一实例 ===============");
}
}
}
输出结果
三、单例模式—懒汉式
- 懒汉式,顾名思义,该单例类十分懒,因此在需要用到实例的时候才会去创建实例,是 线程不安全 的
- 只要不使用,就不会创建实例,可以节省内存空间,但在需要用的时候,需要创建实例,增加了时间消耗
- 是典型的 时间换空间 的示例
懒汉式类
/**
* @Title: LazySingletonPattern.java
* @Description: 懒汉式类
* @Author: xj
* @Date: 2018/10/20 10:03
*/
public class LazySingletonPattern {
// volatile关键字用于防止JVM进行指令重排
private volatile static LazySingletonPattern lazySingletonPattern;
// 私有化构造函数,防止通过new的方式来创建实例
private LazySingletonPattern() {
System.out.println("懒汉式 ---> 调用构造方法");
}
// 提供一个公共的,静态的,返回本类实例的方法
public static LazySingletonPattern getInstance() {
if (null == lazySingletonPattern) {
synchronized (LazySingletonPattern.class) {
if (null == lazySingletonPattern) {
System.out.println("懒汉式 ---> 创建实例");
lazySingletonPattern = new LazySingletonPattern();
} else {
System.out.println("懒汉式 ---> 实例已被创建");
}
}
}
System.out.println("懒汉式 ---> 调用静态方法返回实例");
return lazySingletonPattern;
}
}
测试类
/**
* @Title: Test.java
* @Description: 测试类
* @Author: xj
* @Date: 2018/10/20 10:16
*/
public class Test {
public static void main(String[] args) {
// 懒汉式
System.out.println("=============== 单例模式: 懒汉式 ===============");
LazySingletonPattern l1 = LazySingletonPattern.getInstance();
LazySingletonPattern l2 = LazySingletonPattern.getInstance();
if (l1 == l2) {
System.out.println("=============== l1与l2为同一实例 ===============");
}
}
}
输出结果
- 懒汉式中,声明静态成员时,使用了 volatile 关键字,其提供了 “内存屏障” 的方式来防止指令被重排序,为了实现volatile的内存语义,编译器在生成字节码时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序
- 此外,还使用了 Double Check Locking (双重锁定) + synchronized 关键字,来确保操作的原子性
四、参考链接
- 关于 volatile 关键字的作用以及 指令排序 的相关知识,小伙伴们可以参考以下链接
- 为什么volatile不能保证原子性而Atomic可以?
- Java并发:volatile内存可见性和指令重排

浙公网安备 33010602011771号