先列举最常见的单例模式:
1:懒汉式
Class Singleton{ private static Singleton instance = null; private Singleton(){} public static Singleton getInstance(){ if(instance == null){ instance = new Singleton(); } return instance; } }
此方法线程不安全
2:饿汉式
public class SingleTon{ private static SingleTon INSTANCE = new SingleTon(); private SingleTon(){} public static SingleTon getInstance(){ return INSTANCE; }}
加载类时就初始化对象,占用内存
3:双锁检验懒汉式
class Singleton{ private static Singleton instance; private Singleton(){} public static Singleton getInstance(){ if(instance==null){ synchronized(Singleton.class){ if(instance==null){ instance = new Singleton(); } } } return instance; } }
此方法看似解决了线程安全问题,实际上还存在隐患,可能出现线程a还在初始化instance但未完全初始化时,线程b就通过instance!=null判断返回了这个instance对象。
这是由于JVM的重排序,导致instance已经指向了一块内存,但这块内存并没有初始化。其根本原因是对象实例化不是原子操作,其顺序可能被重排,可分为三步:
- 1.分配一块内存空间
- 2.在这块内存上初始化一个DoubleCheckLock的实例
- 3.将声明的引用instance指向这块内存
具体原因可参考:https://blog.csdn.net/zy13608089849/article/details/82703192
这里可以给instance加上volatile修饰符禁止jvm重排解决。
其他线程安全的方法还有:
4:静态内部类
public class SingleTon{ private SingleTon(){} private static class SingleTonHoler{ private static SingleTon INSTANCE = new SingleTon(); } public static SingleTon getInstance(){ return SingleTonHoler.INSTANCE; } }
由于内部类只会在getInstance()时才会被加载,且只加载一次,因此也可以看作是线程安全的懒汉式
5:枚举
public enum Singleton{ INSTANCE; }
大概是最简单的单例了哈哈