懒汉式写法,包含演进过程,通过双重检查和静态方法实现的方式保证线程安全;
//1.懒汉式(延迟加载)
//线程不安全写法
public class Methods{
private Methods{};
private static Methods instance = null;
//获取单例
public static Methods getInstance(){
if(instance==null){
return new Methods();
}
return instance;
}
}
//双重检查方式写法
public class Methods{
private Methods{};
private static Methods instance = null;
//获取单例
public static Methods getInstance(){
if(instance==null){
//如果为null也只有一个线程能进入,不满足条件最终走最外部的那个return
synchronized(Methods.class){
if(instance==null){
return new Methods();
}
}
}
return instance;
}
}
//上述的写入在synchronized里面的代码 存在指令重排序的问题,如果先给instance分配内存地址,然后再去实例化对象,这样先进入到线程已经复制,后进入的线程也无法获取到
//双重加锁 防止指令重排序的写法
public class Methods{
private Methods{};
private volatile static Methods instance = null;
//获取单例
public static Methods getInstance(){
if(instance==null){
//如果为null也只有一个线程能进入,不满足条件最终走最外部的那个return
synchronized(Methods.class){
if(instance==null){
return new Methods();
}
}
}
return instance;
}
}
//基于静态内部类的实现 原理是类初始化的时候,只有一个类能获取到类初始化锁
public class Methods{
private Methods{};
private static class InnerMethods{
private static Methods methods = new Methods{};
}
public static Methods getInstance(){
return InnerMethods.methods;
}
}
饿汉式写法很简单,饿汉式最大的缺点就是没有延迟加载的效果,如果一个单例从项目启动一直都没有被使用,会造成很大内存浪费;
//2.饿汉式(没有延迟加载的效果)
public class Methods{
private final static Methods methods = new Methods();
private Methods{};
public static Methods getInstance(){
return methods;
}
}
既然饿汉式和懒汉式的写法看似已经非常完美了,那为何还需要强调使用枚举来实现单例模式;原因在于上述两种实现方式有两个很大的问题,就是序列化破坏和反射攻击;
序列化攻击:利用单例获取的对象进行序列化,然后在将序列化的对象反序列化,对比两者不相同;
反射攻击:同样的道理,通过反射获取到的对象和实例化的对象,两者不相等;
小伙伴可以亲自试一试便知道,至于具体是什么原因导致的,由于篇幅原因,这里不赘述,小伙伴可以参考网上相关的资料;
那么我们如何才能解决上述两个问题呢,那就是我们这篇博客的主题,通过枚举的方式实现单例模式;代码也非常简洁;
public enum Methods{
INSTANCE;
public static Methods getInstance(){
return INSTANCE;
}
}