前言
设计模式系列目录
单例模式(Singleton Pattern) 确保一个类只有一个实例,并提供全局访问点。两种实现:饿汉模式(快速加载)
、懒汉模式(延迟加载到使用时)
小声bb:同志们,场景自己琢磨吧。这里说啥也不如自己实践一下。
类图
类图好像有点抽象?我之前在类图的几种线条中说过这个自关联
。理解不了也没关系,我们直接看代码。
代码
代码提交到Github上了
https://github.com/software-market/design-pattern(欢迎star,谢谢各位大佬。)
单例模式代码不多,在这展示一下。掌握精髓后是不是感觉“就这”?对,一点都不神秘。
饿汉模式
public class Singleton {
// 构造方法私有化,保证单例
private Singleton() {
}
// 自己创建一个实例,私有静态
private static final Singleton instance = new Singleton();
// 全局访问点
public static Singleton getInstanse() {
return instance;
}
public String sayHello() throws Exception {
return LocalDate.now() + " - hello";
}
}
懒汉模式 - 初级版本
懒汉模式,延迟实例创建。但存在线程安全的问题:并发环境下会创建出多个实例
public class Singleton {
// 构造方法私有化,保证单例
private Singleton() {
}
// 自己创建一个实例,私有静态
private static Singleton instance;
// 全局访问点
public static Singleton getInstanse() {
if(instance == null)
instance = new Singleton(); // 延迟加载
return instance;
}
public String sayHello() throws Exception {
return LocalDate.now() + " - hello";
}
}
懒汉模式 - 究极版本
双重检查锁(DCL)版本。
第一重缩小锁粒度,减少锁操作。
第二步,这步在同步区域,保证实例为空才会去创建。
volatile关键字,这一步保证new操作是一个原子性操作,避免被指令重排影响,感兴趣的自行百度。
public class Singleton {
//构造方法私有化,保证单例
private Singleton() {
}
// 自己创建一个实例,私有静态
private static volatile Singleton instance;
// 全局访问点
public static Singleton getInstanse() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
public String sayHello() throws Exception {
return LocalDate.now() + " - hello";
}
}
懒汉模式 - 内部类版本
public class Singleton {
// 构造方法私有化,保证单例
private Singleton() {
}
// 内部类的方式创建
private static final class creater {
private static final Singleton INSTANCE = new Singleton();
}
// 全局访问点
public static Singleton getInstanse() {
return creater.INSTANCE;
}
public String sayHello() throws Exception {
return LocalDate.now() + " - hello";
}
}
以下测试代码示例,多线程问题不在此赘述。
public class SingletonTest {
@Test
public void test() {
try {
String s = Singleton.getInstanse().sayHello();
System.out.println(s);
} catch (Exception e) {
e.printStackTrace();
}
}
}
输出如下
2021-02-02 - hello
总结
- 多线程问题不能等到出现时才考虑,我们必须假定所有的程序都是多线程的,然后选择合适的方案去创建单例。
- 在1.4及更早版本的java中,volatile关键字的实现会导致它失效。
- 不同的类加载器,也会导致实例被重复创建。
- java1.2必须使用内存来保存单例的引用,不然会被垃圾回收机制给干掉。