文章目录
- 1.单例模式的作用
- 2.单例模式的适用场景
- 3.饿汉式
- 静态常量(可用)
- 静态代码块(可用)
- 4.懒汉式
- 线程不安全(不可用)
- 同步方法(线程安全,但不推荐用)
- 同步代码块(线程不安全,不可用)
- 双重检查 + volatile(推荐用)
- 静态内部类(推荐用)
- 枚举(推荐用)
1.单例模式的作用
为什么需要单例?
- 节省内存和计算
- 保证结果正确
- 方便管理
2.单例模式的适用场景
- 无状态的工具类:比如日志工具类,不管是在哪里使用,我们需要的只是它帮我们记录日志信息,除此之外,并不需要在它的实例对象上存储任何状态,这时候我们就只需要一个实例对象即可。
- 全局信息类:比如我们在一个类上记录网站的访问次数,我们不希望有的访问被记录在对象 A 上,有的却记录在对象 B 上,这时候我们就让这个类成为单例。
3.饿汉式
静态常量(可用)
/** * 饿汉式(静态常量)(可用) */public class Singleton1 {// 由于加了static关键字,根据JVM的规定,在类加载的时候就会完成INSTANCE的实例化,这样就避免了线程同步问题private final static Singleton1 INSTANCE = new Singleton1();// 构造函数是私有的private Singleton1() {}public static Singleton1 getInstance() {return INSTANCE;}}
静态代码块(可用)
/** * 饿汉式(静态代码块)(可用) */public class Singleton2 {private final static Singleton2 INSTANCE;// 与上一种写法类似,由JVM保证了线程安全static {INSTANCE = new Singleton2();}// 构造函数是私有的private Singleton2() {}public static Singleton2 getInstance() {return INSTANCE;}}
4.懒汉式
线程不安全(不可用)
/** * 懒汉式(线程不安全)(不可用) */public class Singleton3 {private static Singleton3 instance;// 构造函数是私有的private Singleton3() {}public static Singleton3 getInstance() {// 这种写法是线程不安全的,不可用if (instance == null) {instance = new Singleton3();}return instance;}}
同步方法(线程安全,但不推荐用)
/** * 懒汉式(线程安全)(不推荐用) */public class Singleton4 {private static Singleton4 instance;// 构造函数是私有的private Singleton4() {}// 这种写法虽然是线程安全的,但是效率太低,不推荐用public synchronized static Singleton4 getInstance() {if (instance == null) {instance = new Singleton4();}return instance;}}
同步代码块(线程不安全,不可用)
/** * 懒汉式(线程不安全)(不可用) */public class Singleton5 {private static Singleton5 instance;// 构造函数是私有的private Singleton5() {}public static Singleton5 getInstance() {// 这种写法并不是线程安全的,不可用if (instance == null) {synchronized (Singleton5.class) {instance = new Singleton5();}}return instance;}}
双重检查 + volatile(推荐用)
优点:线程安全,延迟加载,效率较高。
/** * 双重检查 + volatile(推荐用) */public class Singleton6 {// volatile防止重排序private volatile static Singleton6 instance;// 构造函数是私有的private Singleton6() {}public static Singleton6 getInstance() {// 双重检查保证线程安全if (instance == null) {synchronized (Singleton6.class) {if (instance == null) {instance = new Singleton6();}}}return instance;}}
为什么要用 volatile?
新建对象 rs = new Resource()
实际上有 3 个步骤:
- construct empty resource()
- call constructor
- assign to rs
如下图所示,重排序会带来NPE问题(NullPointerException, 空指针异常),而使用 volatile 可以防止重排序。
静态内部类(推荐用)
/** * 静态内部类(线程安全,懒加载)(推荐用) */public class Singleton7 {// 构造函数是私有的private Singleton7() {}// 由JVM的规定可知,这种写法同时满足了线程安全和懒加载两个优点private static class SingletonInstance {private static final Singleton7 INSTANCE = new Singleton7();}public static Singleton7 getInstance() {return SingletonInstance.INSTANCE;}}
枚举(推荐用)
单例模式的书写:
/** * 枚举(线程安全,懒加载)(推荐用) */public enum Singleton8 {INSTANCE;public void whatever() {}}
单例的使用:
Singleton8.INSTANCE.whatever();
哪种单例的实现方案最好?
Joshua Bloch 大神在《Effective Java》中明确表达过的观点:使用枚举实现单例的方法虽然还没有广泛采用,但是单元素的枚举类型已经成为实现 Singleton 的最佳方法。
- 写法简单
- 线程安全有保障
- 懒加载
- 避免反序列化破坏单例