[2022.5.12]设计模式-单例
1、singleton 单例模式
保证一个对象只能有一个实例
单例的几种实现方式
1. 给成员变量赋值时就创建对象
/** * 饿汉式 * 类加载到内存后,就实例化一个单例,JVM保证线程安全 * 简单实用,推荐使用! * 唯一缺点:不管用到与否,类装载时就完成实例化 */ public class Mgr01 { private static final Mgr01 INSTANCE = new Mgr01(); public static Mgr01 getInstance() { return INSTANCE; } }
2. 利用初始化代码块创建对象
public class Mgr02 { private static final Mgr02 INSTANCE; static { INSTANCE = new Mgr02(); } public static Mgr02 getInstance() { return INSTANCE; } }
3. 懒汉式,调用时创建对象。会有线程安全问题
/** * lazy loading * 也称懒汉式 * 虽然达到了按需初始化的目的,但却带来线程不安全的问题 */ public class Mgr03 { private static Mgr03 INSTANCE; private Mgr03() { } public static Mgr03 getInstance() { //可能多个线程同时到达此处会有线程安全问题 if (INSTANCE == null) { INSTANCE = new Mgr03(); } return INSTANCE; } }
4. 懒汉式,为了解决3的问题对getInstance方法进行加锁。效率低下
public class Mgr04 { private static Mgr04 INSTANCE; public static synchronized Mgr04 getInstance() { if (INSTANCE == null) { INSTANCE = new Mgr04(); } return INSTANCE; } }
5. 对代码块进行加锁。这个一个失败的案例,任然无法解决线程安全问题
public class Mgr05 { private static Mgr05 INSTANCE; public static Mgr05 getInstance() { //判断为空未加锁任然有可能多个线程同时进入 if (INSTANCE == null) { //妄图通过减小同步代码块的方式提高效率,然后不可行 synchronized (Mgr05.class) { INSTANCE = new Mgr05(); } } return INSTANCE; } }
6. 双重检查保证线程安全(懒汉式较完美的写法)
public class Mgr06 { /** *添加 volatile 的原因? *由于操作的指令重排序问题。在创建对象时可能未完成对象的初始化 *就把引用指向了INSTANCE。导致其他线程拿到了一个半成品对象。 *为了避免这种问题所以要加volatile * */ private static volatile Mgr06 INSTANCE; public static Mgr06 getInstance() { //第一步判断不加锁,效率更高 if (INSTANCE == null) { //双重检查 synchronized (Mgr06.class) { //拿到锁标记再次判断。此时线程安全 if(INSTANCE == null) { INSTANCE = new Mgr06(); } } } return INSTANCE; } }
7. 利用类加载的特性保证线程安全(一个类只会加载一次)
/** * 静态内部类方式 * JVM保证单例 * 加载外部类时不会加载内部类,这样可以实现懒加载 */ public class Mgr07 { private static class Mgr07Holder { private final static Mgr07 INSTANCE = new Mgr07(); } public static Mgr07 getInstance() { return Mgr07Holder.INSTANCE; } }
8. 利用枚举类实现。(枚举类没有构造方法。能够避免反序列化)
public enum Mgr08 { INSTANCE; public void m() { } public static void main(String[] args) { for(int i=0; i<100; i++) { new Thread(()->{ System.out.println(Mgr08.INSTANCE.hashCode()); }).start(); } } }
上一篇:
通过多线程提高代码的执行效率例子