前言
懒汉模式虽然起到了延迟加载的,但是总的来说这个东西还不是很靠谱,我们只能继续探寻下去。今天我们来看看剩下的三种:双重检查、静态内部类、枚举方式。
单例模式(双重检查)
双重检查,主要是使用 volatile 关键字,声明我们的类中的静态属性
1 | package com.yubulang.type06; |
2 | |
3 | public class Singleton { |
4 | private Singleton() { |
5 | } |
6 | |
7 | private static volatile Singleton instance; |
8 | |
9 | // 提供一个静态的公有方法,加入双重检查代码,解决线程安全问题,同时解决懒加载问题 |
10 | // 同时保证了效率,推荐使用 |
11 | public static Singleton getInstance() { |
12 | if (instance == null) { |
13 | synchronized (Singleton.class) { |
14 | if (instance == null) { |
15 | instance = new Singleton(); |
16 | } |
17 | } |
18 | } |
19 | |
20 | return instance; |
21 | } |
22 | } |
优缺点分析:
- 双重检查概念是多线程开发中常使用的,如上面代码所示,我们进行了两次 if (instance == null)检查,这样就可以保证线程安全了。
- 这样实例化代码只用执行一次,后面再次访问时,判断 if (instance == null),直接 return 实例化对象,也避免的反复进行方法同步。
- 线程安全;延迟加载;效率较高
- 结论:在实际开发中,推荐使用这种单例设计模式。
单例模式(静态内部类)
这个方式主要利用了静态内部类的特点,我们会在结论告诉大家为啥。
1 | package com.yubulang.type07; |
2 | |
3 | // 静态内部类, 推荐使用 |
4 | public class Singleton { |
5 | private Singleton() { |
6 | } |
7 | |
8 | // 写一个静态内部类,该类中有一个静态的属性 Singleton |
9 | private static class SingletonInstance { |
10 | private static final Singleton INSTANCE = new Singleton(); |
11 | } |
12 | |
13 | // 提供一个静态的共有方法,直接返回 |
14 | public static Singleton getInstance() { |
15 | return SingletonInstance.INSTANCE; |
16 | } |
17 | } |
优缺点分析:
- 这种方式采用了类装载的机制来保证初始化实例时只有一个线程
- 静态内部类方式在 Singleton 类被装载时并不会立即实例化,而是在需要实例化时,调用 getInstance 方法,才会装载 SingletonInstance 类,从而完成 Singleton 的实例化。
- 类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM 帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。
- 优点:避免了线程不安全,利用静态内部类特点实现延迟加载,效率高
- 结论:推荐使用
单例模式(枚举方式)
枚举是 jdk 1.5 提供的,我们可以利用它的特点来进行单例的声明
1 | package com.yubulang.type08; |
2 | |
3 | // 使用枚举方法,可以实现单例,推荐 |
4 | enum Singleton { |
5 | INSTANCE; |
6 | |
7 | public void method01() { |
8 | System.out.println("ok"); |
9 | } |
10 | } |
优缺点分析:
- 这借助 JDK 1.5 中添加的枚举来实现单例模式,不仅可以避免多线程同步问题,而且还能防止反序列化重新创建新的对象。
- Effective Java 作者 Josh Bloch 提倡的方式
- 结论:推荐使用
总结
我们说了八种单例模式的实现方式,Java 真的吐槽一下方式太多了哈哈哈,那么这么多方式我们只需要记住推荐使用的那几个,我们推荐使用:
- 饿汉式(静态代码块)——在确定肯定会被使用到时,这样可以避免内存浪费
- 双重检查
- 静态内部类
- 枚举方式
单例模式的注意事项
- 单例模式保证了系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式提高系统性能
- 当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用 new
- 单例模式使用的场景:需要频繁的进行创建和销毁的进行创建和销毁的对象、创建对象时耗时过多或耗费资源过多(即:重量级对象),但又经常用到的对象、工具类对象、频繁访问数据库或文件的对象(比如数据源、session 工厂等)