彻底玩转单例模式
程序员文章站
2024-03-24 09:41:34
...
饿汉式单例
// 饿汉式单例
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 Hungry(){
}
private final static Hungry HUNGRY = new Hungry();
public static Hungry getInstance(){
return HUNGRY;
}
}
懒汉单例
public class LazyMan {
private static LazyMan(){
System.out.println(Thread.currentThread().getName()+"ok")
}
private static LazyMan lazyMan;
public static LazyMan getInstance(){
if (lazyMan==null){
lazyMan=new LazyMan();
}
return lazyMan;
}
//多线程并发
public static void main(String[] args){
for(int i=0;i<10;i++){
new Thread(()->{
LazyMan.getInstance();
}).start();
}
}
}
单线程下懒汉模式没问题,多线程下偶尔成功偶尔失败。
运用双重检测锁改进使多线程下懒汉模式也可以:
public class LazyMan {
private static LazyMan(){
System.out.println(Thread.currentThread().getName()+"ok")
}
private volatile static LazyMan lazyMan;
// 双重检测锁模式的 懒汉式单例 DCL懒汉式
public static LazyMan getInstance(){
if (lazyMan==null){//第一重检测
synchronized (LazyMan.class){
if (lazyMan==null){//第二重检测
lazyMan = new LazyMan(); // 不是一个原子性操作
}
}
}
return lazyMan;
}
}
// lazyMan = new LazyMan(); // 不是一个原子性操作
/**
* 1. 分配内存空间
* 2、执行构造方法,初始化对象
* 3、把这个对象指向这个空间
*可能会出现指令重排
* 123
* 132 A
* B // 此时lazyMan还没有完成构造
*/
//所以要把 private static LazyMan lazyMan;加上volatile才算双重检测锁
静态内部类:
// 静态内部类
public class Holder {
//构造器私有
private Holder(){
}
//调用内部类的对象
public static Holder getInstace(){
return InnerClass.HOLDER;
}
public static class InnerClass{
//创建对象
private static final Holder HOLDER = new Holder();
}
}
以上的所有单例都不安全,因为可以通过反射破坏。
解决反射破坏单例模式的问题:
// 懒汉式单例
// 道高一尺,魔高一丈!
public class LazyMan {
//定义一个关键字加密来阻止反射破坏
private static boolean qinjiang = false;
//构造器私有
private LazyMan(){
synchronized (LazyMan.class){
if (qinjiang == false){
qinjiang = true;
}else {
throw new RuntimeException("不要试图使用反射破坏异常");
}
}
}
private volatile static LazyMan lazyMan;
// 双重检测锁模式的 懒汉式单例 DCL懒汉式
public static LazyMan getInstance(){
if (lazyMan==null){//第一重检测
synchronized (LazyMan.class){
if (lazyMan==null){//第二重检测
lazyMan = new LazyMan(); // 不是一个原子性操作
}
}
}
return lazyMan;
}
// 反射!
public static void main(String[] args) throws Exception {
// LazyMan instance = LazyMan.getInstance();
//找到加密的关键字
Field qinjiang = LazyMan.class.getDeclaredField("qinjiang");
qinjiang.setAccessible(true);
//空参构造器
Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);//无视了私有构造器
//通过反射创建对象
LazyMan instance = declaredConstructor.newInstance();
//把加密的值改回来,又可以破坏单例
qinjiang.set(instance,false);
LazyMan instance2 = declaredConstructor.newInstance();
System.out.println(instance);
System.out.println(instance2);
}
}
反射不能破坏枚举的单例
// enum 是一个什么? 本身也是一个Class类
public enum EnumSingle {
INSTANCE;
public EnumSingle getInstance(){
return INSTANCE;
}
}
class Test{
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
EnumSingle instance1 = EnumSingle.INSTANCE;
Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
declaredConstructor.setAccessible(true);
EnumSingle instance2 = declaredConstructor.newInstance();
// NoSuchMethodException: com.kuang.single.EnumSingle.<init>()
System.out.println(instance1);
System.out.println(instance2);
}
}
枚举类型的最终反编译源码(通过jad工具反编译):
本文为学习狂神说的JUC课程做的笔记,课程地址:https://www.bilibili.com/video/BV1B7411L7tE?p=32