第三章:单例设计模式
一、设计模式概述
1)《设计模式》是经典书籍,作者是 Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides Design(俗称:“四人组 GOF”)
2)设计模式——design pattern,是某类问题的通用解决方案
3)设计模式分为三种类型,共 23 种
- 创建者模式:单例模式、抽象工厂模式、原型模式、建造者模式、工厂模式
- 结构型模式:适配器模式、桥接模式、装饰者模式、组合模式、外观模式、享元模式、代理模式
- 行为型模式:模板方法模式、命令模式、访问者模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式
二、单例设计模式介绍
采取一定的方法保证在整个软件的运行过程中,某个类只能存在一个对象实例,该类会提供一个获取其对象实例的静态方法
三、单例设计模式七种方式
1. 饿汉式(静态常量)
所谓饿汉式,就是在类加载时,对象实例就创建好了
public class Singleton {
// 类内部创建对象
private final static Singleton instance = new Singleton();
// 构造器私有
private Singleton() {}
// 提供返回实例的静态方法
public static Singleton getInstance() {
return instance;
}
}
测试一下是否真的只有一个实例(其他方式测试略)
public static void main(String[] args) {
Singleton instance1 = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println(instance1.hashCode());
System.out.println(instance2.hashCode());
System.out.println(instance1 == instance2);
}
/*************** output ***************
460141958
460141958
true
**************************************/
优点 | 缺点 |
---|---|
写法简单 | 没有做到懒加载(Lazy Loading) |
轻松避免线程同步问题(‾◡◝) | 如果自始至终都没用到实例,则会造成内存浪费 |
如果类中除了获取实例的静态方法就没有其他静态方法的话,饿汉式就很好( ̄_, ̄ )
2. 饿汉式(静态代码块)
public class HungrySingleton {
// 构造亲私有
private HungrySingleton() {}
// 类内部创建实例
private final static HungrySingleton instance;
static {
try {
instance = new HungrySingleton();
} catch (Throwable e) {
// 必须抛出一个错误,不然会报 final 未初始化
// 当然也可以不加 final 关键词
throw new Error();
}
}
// 提供一个静态方法返回实例
public static HungrySingleton getInstance() {
return instance;
}
}
优缺点同上ㄟ( ▔, ▔ )ㄏ
3. 懒汉式(线程不安全)
所谓懒汉式就是在类加载时并没有创建实例,只有在第一次获取实例对象时才创建
public class LazySington {
private static LazySington instance;
private LazySington() {}
public static LazySington getInstance() {
if (instance == null) {
instance = new LazySington();
}
return instance;
}
}
优点 | 缺点 |
---|---|
做到了懒加载 | 线程不安全 |
想象一下有两个线程 A 和 B,当 A 判断 instance 为空后,正要 new 时发生了线程上下文切换,cpu 时间片分给了 B,B new 好对象后,又切换成 A 运行,这时 A 又new 了一遍。
所以上述方式不推荐使用(⊙ˍ⊙)
4. 懒汉式(线程安全,同步方法)
public class LazySington {
private static LazySington instance;
private LazySington() {}
public static synchronized LazySington getInstance() {
if (instance == null) {
instance = new LazySington();
}
return instance;
}
}
优点 | 缺点 |
---|---|
懒加载 | 效率低 |
线程同步 | |
实际上只有第一次初始化实例时需要加锁,其他阶段加锁毫无卵用,白白降低并发度
5. 懒汉式(线程安全,同步代码块双重检查)
public class LazySington {
private static LazySington instance;
private LazySington() {
}
public static LazySington getInstance() {
if (instance == null) {
synchronized (LazySington.class) {
if (instance == null) {
instance = new LazySington();
}
}
}
return instance;
}
}
优点 | 缺点 |
---|---|
懒加载 | - |
线程安全 | - |
效率高 | - |
推荐使用这种单例模式
6. 静态内部类
public class InnerClassSingleton {
// 构造方法私有化
private InnerClassSingleton() {}
// 静态内部类
private static class SingletonInstance {
private static final InnerClassSingleton INSTANCE = new InnerClassSingleton();
}
// 获取实例的静态方法
public static InnerClassSingleton getInstance() {
return SingletonInstance.INSTANCE;
}
}
优点 | 缺点 |
---|---|
静态内部类并不会因为类的加载而加载 | - |
所以即使类内有其他静态方法也能做到懒加载 | - |
线程安全 | - |
推荐使用这种单例模式
7. 枚举
enum EnumSingleton {
INSTANCE;
public void sayHello() {
System.out.println("hello!");
}
}
枚举单例本质就是饿汉式(静态代码块),所以只要枚举中没有静态方法造成类加载的话可以推荐使用
四、单例模式的适用场景
1)需要频繁创建销毁的对象
2)创建时耗时或耗费大量资源并且经常用到的对象
3)工具类对象
4)频繁访问数据库或文件的对象(如数据源)