单例模式

单例模式

其目的是再全局范围内同一个类只能有一个(或有限数量)实例,并提供一个全局的访问点。

优点

对于占用资源比较大的对象,例如数据源对象,使用单例可以减少内存开销,避免资源多重占用, 严格控制对象的访问

缺点

没有接口,扩展困难(只能修改代码)

单例的基本准则

  • 私有化构造器
  • 提供获取实例的方法

单例模式的分类

  • 懒汉模式
  • 饿汉模式
    • 双重检查模式
  • 注册式单例
  • ThreadLocal
  • 内部类实现单例模式

饿汉式单例

特点

  • 在类加载时就会创建实例,无论该实例是否真正用到。
  • 线程安全(static,由JVM保证线程安全)
/**
 * 饿汉式单例模式
 */
public class HungrySingleton {
    private static final HungrySingleton INSTANCE = new HungrySingleton();

    private HungrySingleton() {
    }

    public static HungrySingleton getInstance() {
        return INSTANCE;
    }
}

懒汉式单例

特点

  • 在真正需要使用对象时才会创建对象实例。
  • 线程不安全(多线程下可能多个线程同时进入null判断)
/**
 * 懒汉式单例
 */
public class LazySingleton {
    private static LazySingleton INSTANCE = null;

    private LazySingleton() {
    }

    public static LazySingleton getInstance() {
        if (INSTANCE == null) { //unsafe 多线程
            INSTANCE = new LazySingleton(); 
        }
        return INSTANCE;
    }
}

线程安全的懒汉式

  • 在方法上直接加锁
public class SynchronizedSingleton {
    private static SynchronizedSingleton instance;

    public SynchronizedSingleton() {
    }

    //直接在方法上加锁,存在一定的性能问题
    public static synchronized SynchronizedSingleton getInstance() {
        if (instance == null) {
            instance = new SynchronizedSingleton();
        }
        return instance;
    }
}
  • 双重检查+锁
/**
 * 双重检查
 */
public class DoubleCheckSingleton {
    //volatile 解决指令重排问题
    private volatile static DoubleCheckSingleton instance = null;

    private DoubleCheckSingleton() {
    }

    public static DoubleCheckSingleton getInstance() {
        if (instance == null) {
            synchronized (DoubleCheckSingleton.class) {
                if (instance == null) { //第二次检查实例
                    instance = new DoubleCheckSingleton();
                }
            }
        }
        return instance;
    }
}

内部类模式

特性

  • 不使用锁
  • 利用了类加载的特性(在使用到的时候才加载且JVM保证了一个类只加载依次即线程安全)
public class InnerClassSingleton {
    private InnerClassSingleton() {
        if (InstanceHolder.instance != null) { //避免反射攻击
            throw new IllegalStateException("不允许反射创建实例");
        }
    }

    public static InnerClassSingleton getInstance() {
        return InstanceHolder.instance;
    }

    private static class InstanceHolder {
        private static InnerClassSingleton instance = new InnerClassSingleton();
    }
}

破坏单例的方式

  • 反射
  • 反序列化

解决

反射

以上所有的单例都可能被反射攻击(直接获取类的构造器来实例化对象)d

public static void main(String[] args) {
    try {
        Constructor<InnerClassSingleton> cons = InnerClassSingleton.class.getDeclaredConstructor(null);
        InnerClassSingleton instance = cons.newInstance();
    } catch (Exception e) {
        e.printStackTrace();
    } 
}

在构造方法中添加非空判断

//inner class
private InnerClassSingleton() {
    if (InstanceHolder.instance != null) { //避免反射攻击
        throw new IllegalStateException("不允许反射创建实例");
    }
}
//懒汉式
private DoubleCheckSingleton() {
    if (instance != null) {
        throw new IllegalStateException("不允许反射构建实例");
    }
}

反序列化

public class SeriableSingleton implements Serializable {
    private SeriableSingleton() {
    }

    private static SeriableSingleton instance = new SeriableSingleton();

    private SeriableSingleton getInstance() {
        return instance;
    }

    //解决反序列化
    //在反序列化时仍然会创建一次对象,但是在jvm中就将反序列化出来的对象使用此方法中的对象将其替换掉了
    private Object readResolve() {
        return instance;
    }
}

ObjectInputStream
image

注册式单例

  • 枚举
    • 枚举不能被反射创建
  • 容器

ThreadLoacl

ThreadLoacl 以当前线程为key, value为用户给定的值, 所以当前线程取值的时候只会取到当前线程设置的值。属于容器式单例

/**
 * 每一个线程一个实例
 */
public class ThreadLocalSingleton {

    private ThreadLocalSingleton() {
    }

    private static final ThreadLocal<ThreadLocalSingleton> instance = new ThreadLocal<ThreadLocalSingleton>() {
        @Override
        protected ThreadLocalSingleton initialValue() {
            return new ThreadLocalSingleton();
        }
    };

    public static ThreadLocalSingleton getInstance() {
        return instance.get();
    }
}
posted @   Dreamsrj  阅读(16)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
点击右上角即可分享
微信分享提示