单例模式
饿汉式 DCL懒汉式 探究
饿汉式
私有构造器 程序一上来就创建好对象 导致-> 可能会浪费内存
//饿汉式单例,私有构造器
public class Hungry {
//一上来就把这个全部加载可能会浪费内存
private byte[] data1 = new byte[1024*1024];
private byte[] data2 = new byte[1024*1024];
private byte[] data3 = new byte[1024*1024];
private byte[] data4 = new byte[1024*1024];
private byte[] data5 = new byte[1024*1024];
private Hungry(){
}
private final static Hungry HUNGRY = new Hungry();//一上来就new
//一上来就把这个对象加载了
public static Hungry getInstance(){
return HUNGRY;
}
}
DCL懒汉式
这样单例下OK 多线程并发有问题
- 分配内存空间
- 执行构造方法,初始化对象
- 把这个对象执向这个空间
正常顺序 123
真实顺序可能是 132
线程A没问题
线程B可能有问题
//懒汉式单例
//结论: 道高一尺,魔高一丈
public class LazyMan {
private static boolean swae_qj = false;//四重检测,加密
//也是私有构造器
private LazyMan(){
//可以在此处解决-反射破坏单例问题,三重检测
synchronized (LazyMan.class){
if (swae_qj == false){//四重检测
swae_qj = true;
}else {
throw new RuntimeException("不要试图使用反射破坏异常");
}
}
// System.out.println(Thread.currentThread().getName() + "ok");
}
private volatile static LazyMan lazyMan;
public static LazyMan getInstance(){
//两次检测要加锁,因为这样会导致线程数量不确定
//双重检测锁模式的 懒汉式单例 DCL懒汉式
if (lazyMan==null){
synchronized (LazyMan.class){
if (lazyMan==null){//lazyman为空在创建
lazyMan = new LazyMan();//不是一个原子性操作
/**
* 1. 分配内存空间
* 2. 执行构造方法,初始化对象
* 3. 把这个对象执向这个空间
*
* 正常顺序 123
* 真实顺序可能是 132 线程A没问题
* 线程B可能有问题
*/
}
}
}
return lazyMan;//此时LazyMan还没有完成构造
}
//反射!
public static void main(String[] args) throws Exception{
// LazyMan instance = LazyMan.getInstance();
Field swae_qj = LazyMan.class.getDeclaredField("swae_qj");
swae_qj.setAccessible(true);//把它也破坏
Constructor<LazyMan> declaredConstructors = LazyMan.class.getDeclaredConstructor(null);
declaredConstructors.setAccessible(true);//无视私有构造器
LazyMan instance = declaredConstructors.newInstance();//通过反射创建对象
swae_qj.set(instance,false);
LazyMan instance2 = declaredConstructors.newInstance();//通过反射创建对象
System.out.println(instance);
System.out.println(instance2);//反射可以破坏单例
}
}
/*
//单线程下OK
//多线程并发
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(()->{
LazyMan.getInstance();
}).start();
}
}
*/
静态内部类
//静态内部类,只要是静态内部类就 构造器私有
public class Holder {
private Holder(){
}
public static Holder getInstance(){
return InnerClass.HOLDER;
}
public static class InnerClass{
private static final Holder HOLDER = new Holder();
}
}
单例不安全,反射
枚举
//enum 是什么? 本身也是一个Class 类
public enum EnumSingle {
INSTANCE;//本身就是单例
public EnumSingle getInstance(){
return INSTANCE;//能保证我这个对象一定是唯一的吗?
}
}
class Test{
public static void main(String[] args) throws Exception{
EnumSingle instance1 = EnumSingle.INSTANCE;
//Cannot reflectively create enum objects
//反射不能破坏Enum的单例
Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
declaredConstructor.setAccessible(true);
EnumSingle instance2 = declaredConstructor.newInstance();
//NoSuchMethodException: com.swae.single.EnumSingle.<init>()
//它说这个类 没有空参的构造器
System.out.println(instance1);
System.out.println(instance2);
}
}
有参构造
枚举类型的最终反编译源码:
结论:反射不能破坏Enum的单例