栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

设计模式之单例模式

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

设计模式之单例模式

模式定义:
        保证一个类只有一个实例,并且提供一个全局访问点

场景:
        重量级的对象,不需要多个实例,如线程池,数据库连接池。

1.懒汉模式

        延迟加载, 只有在真正使用的时候,才开始实例化。

        基本实现:

public class LazySingletonTest {
    public static void main(String[] args) {
        LazySingleton instance1 = LazySingleton.getInstance();
        LazySingleton instance2 = LazySingleton.getInstance();
        System.out.println(instance1==instance2);


    }
}

class LazySingleton{
    private static LazySingleton instance;
    private LazySingleton(){

    }
    public static LazySingleton getInstance(){
        if(instance==null){
            instance = new LazySingleton();
        }
        return instance;
    }
}

单线程情况下没问题,但是多线程下就存在线程安全问题

public class LazySingletonTest {
    public static void main(String[] args) {

        new Thread(()->{
            LazySingleton instance = LazySingleton.getInstance();
            System.out.println(instance);
        }).start();
        new Thread(()->{
            LazySingleton instance = LazySingleton.getInstance();
            System.out.println(instance);
        }).start();
    }
}

class LazySingleton{
    private static LazySingleton instance;
    private LazySingleton(){

    }
    public static LazySingleton getInstance(){
        if(instance==null){
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            instance = new LazySingleton();
        }
        return instance;
    }
}

第一步优化,加锁并双重检查

public  static LazySingleton getInstance(){
    if(instance==null){
        synchronized (LazySingleton.class){
            if(instance==null){
                instance = new LazySingleton();
            }
        }
    }
    return instance;
}

第二部优化,防治指令重排,造成并发情况下出现空指针,加入volatile关键字。

OK,保证线程安全的懒汉模式就是:

class LazySingleton{
    private volatile static LazySingleton instance;
    private LazySingleton(){

    }
    public  static LazySingleton getInstance(){
        if(instance==null){
            synchronized (LazySingleton.class){
                if(instance==null){
                    instance = new LazySingleton();
                }
            }
        }
        return instance;
    }
}
 2.饿汉模式

类加载的 初始化阶段就完成了 实例的初始化 。本质上就是借助于jvm类加载机制,保证实例的唯一性(初始化过程只会执行一次)及线程安全(JVM以同步的形式来完成类加载的整个过程)。

实现:

class HungrySingleton{
    private static  HungrySingleton instance = new HungrySingleton();
    private HungrySingleton(){

    }
    public static HungrySingleton getInstance(){
        return instance;
    }
}
3.静态内部类
  • 本质上是利用类的加载机制来保证线程安全
  • 只有在实际使用的时候,才会触发类的初始化,所以也是懒加载的一种形式。
class InnerClazzSingleton{
    private static class InnerClassHolder{
        private static InnerClazzSingleton instance = new InnerClazzSingleton();
    }
    private InnerClazzSingleton(){

    }
    public static InnerClazzSingleton getInstance(){
        return InnerClassHolder.instance;
    }
}
4.反射攻击

对于上述的懒汉、恶汉、静态内部类的单例模式,均可以通过反射破解,以静态内部类为例:

public class InnerClazzSingletonTest {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Constructor constructor = InnerClazzSingleton.class.getDeclaredConstructor();
        constructor.setAccessible(true);
        InnerClazzSingleton innerClazzSingleton = constructor.newInstance();

        InnerClazzSingleton instance = InnerClazzSingleton.getInstance();
        System.out.println(innerClazzSingleton==instance);
    }
}

class InnerClazzSingleton{
    private static class InnerClassHolder{
        private static InnerClazzSingleton instance = new InnerClazzSingleton();
    }
    private InnerClazzSingleton(){

    }
    public static InnerClazzSingleton getInstance(){
        return InnerClassHolder.instance;
    }
}

  •  对于恶汉模式和静态内部类可以用以下方式防止反射破解,但是懒汉模式不能防止反射破解
class InnerClazzSingleton{
    private static class InnerClassHolder{
        private static InnerClazzSingleton instance = new InnerClazzSingleton();
    }
    private InnerClazzSingleton(){
        if(InnerClassHolder.instance!=null){
            throw new RuntimeException("单例不允许多个实例");
        }
    }
    public static InnerClazzSingleton getInstance(){
        return InnerClassHolder.instance;
    }
}
5.枚举类型

天然不支持反射创建对应的实例,且有自己的反序列化机制。

利用类加载机制保证线程安全。

public enum  EnumSingleton {
    INSTANCE;
}

class EnumSingletonTest{
    public static void main(String[] args) {
        EnumSingleton instance = EnumSingleton.INSTANCE;
        EnumSingleton instance1 = EnumSingleton.INSTANCE;
        System.out.println(instance==instance1);
    }
}
6.序列化

可以利用 指定方法来替换从反序列化流中的数据,枚举类型天然支持反序列化

public class InnerClazzSingletonTest {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, IOException, ClassNotFoundException {
        InnerClazzSingleton instance = InnerClazzSingleton.getInstance();

        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("innerClazzSingleton"));
        oos.writeObject(instance);
        oos.close();

        


    }
}

class InnerClazzSingleton implements Serializable {

    //static final long serialVersionUID = 42L;
    private static class InnerClassHolder{
        private static InnerClazzSingleton instance = new InnerClazzSingleton();
    }
    private InnerClazzSingleton(){
        if(InnerClassHolder.instance!=null){
            throw new RuntimeException("单例不允许多个实例");
        }
    }
    public static InnerClazzSingleton getInstance(){
        return InnerClassHolder.instance;
    }
    
}

序列化后输出到文件里

反序列化后可以看到和我们通过getInstance()拿到的对象不一致,单例变成了多例

加入版本号和readResolve防范

static final long serialVersionUID = 42L;
Object readResolve() throws ObjectStreamException {
    return InnerClassHolder.instance;
}
 7.源码中的应用

 // Spring & JDK
java.lang.Runtime
org.springframework.aop.framework.ProxyFactoryBean
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry
org.springframework.core.ReactiveAdapterRegistry

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/309717.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号