饿汉式
//饿汉式单例
public class Hungry {
private Hungry(){}
private final static Hungry HUNGRY = new Hungry();
public static Hungry getInstance(){
return HUNGRY;
}
}
缺点:浪费资源
懒汉式
DCL懒汉式
//懒汉式单例
public class Lazy {
private Lazy(){}
private static Lazy LAZY;
//双重检测锁模式 懒汉式单例 DCL懒汉式
public static Lazy getInstance(){
if(LAZY == null){
synchronized (Lazy.class){
if(LAZY == null){
LAZY = new Lazy();
}
}
}
return LAZY;
}
}
多线程下保证原子性
在创建对象时,会进行分配内存空间、执行构造方法,初始化对象、把对象指向内存空间。当多个线程创建实例时,可能会出现LAZY不为null,从而跳过指向内存空间的操作,造成实例为虚无的一个状态。所以多线程模式下要加上volatile,,保证原子性操作;
如下:
//懒汉式单例
public class Lazy {
private Lazy(){}
//volatile 保证单次读写的原子性
private volatile static Lazy LAZY;
//双重检测锁模式 懒汉式单例 DCL懒汉式
public static Lazy getInstance(){
if(LAZY == null){
synchronized (Lazy.class){
if(LAZY == null){
LAZY = new Lazy();
}
}
}
return LAZY;
}
}
静态内部类实现
public class Holder {
private Holder(){}
public static Holder getInstance(){
return InnterClass.HOLDER;
}
public static class InnterClass{
private static final Holder HOLDER = new Holder();
}
}
反射机制
Constructor<Lazy> dec = Lazy.class.getDeclaredConstructor(null); dec.setAccessible(true);
无视私有构造器,创建对象
解决办法://在构造器中加上锁 private Lazy(){} //改为 private Lazy(){ synchronized (Lazy.class){ if(LAZY != null){ throw new RuntimeException(); } } }
但是如果多个对象都是用反射创建,此方法就没有用了。
解决办法:
- 1.创建一个私有变量,例如:
private static boolean isNew = false; private Lazy(){ synchronized (Lazy.class){ if(!isNew ){ isNew = true; } if(LAZY != null){ throw new RuntimeException(); } } }
- 2.使用枚举创建懒汉单例,java源码中表示枚举类不能被反射
public enum EnumSingle { INSTANCE; public EnumSingle getInstance(){ return INSTANCE; } }