饿汉式:类加载时就进行了初始化
缺点:要是这个类从未被使用,造成了资源浪费
其变种有静态代码块方式,但是优缺点还是和饿汉式一样
懒汉式:在调用获取实例方法时才初始化
优点:实现了懒加载
缺点:只能在单线程下使用
其变种有改进double-check,这里必须要用双重检查,如果没有第二层检查的时候还是会出现多个实例的情况(线程一线程二同时通过了第一重检查,两个线程先后去实例化,第一个更不能去掉了,要不然大量线程等待进去判断)
double-check:
还是会存在问题:先了解下new一个对象的三个步骤,其中第二步和第三步的顺序是可能同时或者颠倒执行的,所以会出现下面问题:(需要使用volatile加在对象上,防止这种重排序的发生)
静态内部类式:
与饿汉式的区别就是,这种不会再类加载的时候马上初始化,而是在调用获取方法时再实例化,而且利用jvm的自己的特性实现多线程安全
静态内部类的方法和双重检查式都是比较好的实现方法,但是它们不能防止被反序列化(或反射),生成多个实例,来破坏单例,于是再看看枚举的写法
枚举式:(不常见但十分推荐)
jvm对枚举的序列化时有规定,仅仅是将枚举对象的name属性输出到结果中,在反序列化时是通过java.lang.Enum的valueOf(),来根据名字查找对象,而不是新建一个新的对象。反射时要是枚举类时会直接报错,所以保证不会有任何情况破坏单例
工作中要是有全局信息类和无状态工作类等场景的时候推荐使用枚举的写法来实现单例模式,《Efficient Java》一书中也推荐过这种写法
原文:https://www.cnblogs.com/shineipangtuo/p/12395991.html