先列举最常见的单例模式:
一:饿汉式
1、直接创建适合简单的单例模式:
public class SingleTon{ private static SingleTon INSTANCE = new SingleTon(); private SingleTon(){} public static SingleTon getInstance(){ return INSTANCE; }}
2、适合需要较复杂的初始化的单例模式:
public class SingleTon{ private final static SingleTon INSTANCE ; private String info; static { //初始化参数 } private SingleTon(String info){ this.info = info; } public static SingleTon getInstance(){ return INSTANCE; }}
二:懒汉式
Class Singleton{ private static Singleton instance = null; private Singleton(){} public static Singleton getInstance(){ if(instance == null){ instance = new Singleton(); } return instance; } }
此方法线程不安全
1、双锁检验懒汉式
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重排解决。
其他线程安全的方法还有:
2、静态内部类
public class SingleTon{ private SingleTon(){} private static class SingleTonHoler{ private static SingleTon INSTANCE = new SingleTon(); } public static SingleTon getInstance(){ return SingleTonHoler.INSTANCE; } }
由于内部类只会在getInstance()时才会被加载,且只加载一次,因此也可以看作是线程安全的懒汉式
3、枚举
public enum Singleton{ INSTANCE; }
大概是最简单的单例了哈哈