结合JDK,Spring,Mybatis,Netty,Tomcat,Dubbo等经典框架的源码对设计模式展开分析(二)

本文详细介绍了单例模式的概念、应用场景和实现方式,包括饿汉式、懒汉式、双重锁验证单例模式、静态内部类实现以及枚举式单例模式。还探讨了如何防止单例模式被反射破坏,以及对象创建的其他方式如克隆、序列化和反射。
摘要由CSDN通过智能技术生成

单例模式(Singleton Pattern)

定义:确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点

应用场景:例数据库连接池,ServletContext,Spring框架中的ApplicationContext

需要频繁创建的一些类,使用该模式可以降低系统的内存,GC压力

实例化代价昂贵的对象

频繁进行数据库访问和IO操作的对象

使用方式:私有化构造方法,提供一个静态全局访问方法来返回对象,保证对象只会实例化一次

分支实现:

饿汉式:对象初始化在类加载阶段就进行绝对的线程安全,如使用静态代码块,缺点是单例对象不管会不会使用到,都会创建对象而浪费了内存空间。

懒汉式:在全局访问点被调用时,才会去第一次初始化对象,但是在多线程环境下会出现线程安全问题,因为在多个线程几乎同时调用getinstance()方法时,判空条件会同时满足,并都会去实例化对象从而先后实例化出两个单例对象

(tip:线程调试模式?)

给getinstance()方法加上synchronized及可以解决线程安全问题,但是当在高并发环境下由于锁定的是class对象,一调用静态方法,便可能会导致大批线程阻塞,从而导致性能下降

双重锁验证单例模式:

给单例静态属性加上volatile修饰,避免new操作的指令重排,getinstance方法中第一次判空减少高并发环境下同时进入后面synchronized阻塞的机会,在进入之后第二次判空判断是否单例对象还未初始化,这种方式优化了性能压力,同时兼顾线程安全问题

package com.Singleton;

/**
 * @author yangxiaozhen
 * @date 2022/5/4-23:13
 */
public class LazyDoubleCheckSingleton {
    private volatile static LazyDoubleCheckSingleton instance;

    private LazyDoubleCheckSingleton() {

    }

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

            }
        }
        return instance;
    }
}

更好的解决方案:采用静态内部类的方式:

package com.Singleton;

/**
 * @author yangxiaozhen
 * 内部类形式实现单例模式解决线程安全问题和锁的性能问题
 * @date 2022/5/4-23:46
 */
public class LazyStaticInnerClassSingleton {
    private LazyStaticInnerClassSingleton() {

    }

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

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

(tip:内部类的类加载与其外部内无关,它只在确认被使用时(如调用静态变量,或静态方法时)才进行类加载并只进行一次)

但是简单的这样实现,还是可能被反射破坏掉单例模式的初衷因为私有的构造方法可以在反射中破环掉访问限权进而暴力初始化-->继续升级,在构造方法中判断内部内的静态单例变量是否已初始化(判空),若已初始化过,则抛出runtimeException

目前最优雅的单例模式实现是:枚举式单例模式(利用枚举的语法优势)

通过反编译可以看到默认生成了一段静态代码块与饿汉式实现类似

package com.Singleton;

/**
 * @author yangxiaozhen
 * @date 2022/5/5-22:10
 */
public enum EnumSingleton {
    INSTANCE;
    private Object date;

    public Object getDate() {
        return date;
    }

    public void setDate(Object date) {
        this.date = date;
    }
    public static EnumSingleton getInstance(){
        return INSTANCE;
    }
}
static
    {
        INSTANCE=new EnumSingleton("INSTANCE",0);
        $VALUE = (new EnumSingleton[]{
        INSTANCE
        )};
    }

 

package com.Singleton;

/**
 * @author yangxiaozhen
 * @date 2022/5/6-21:25
 * 最优美的单例模式写法,不会被new 反射 序列化 和clone破坏掉
 */
public class SingletonObjectByEnum {

    private SingletonObjectByEnum(){

    }

    enum singleton{
        SINGLETON;

        private final SingletonObjectByEnum instance;

        singleton(){
            instance = new SingletonObjectByEnum();
        }

        private SingletonObjectByEnum getInstance(){
            return instance;
        }
    }
    public static SingletonObjectByEnum getInstance(){
        return singleton.SINGLETON.instance;
        //return singleton.SINGLETON.getInstance;
    }
}

还有容器式单例模式实现,但是个人感觉没有多大意义,若用hashmap/concurrenthashmap实现则多线程环境下还是可能被破坏,用hashtable实现的话有影响性能 

 其他知识点:

1.创建一个对象的方式有:new、克隆、序列化、反射

2.虽然clone()是Object的方法,也就是说每个对象都拥有一个克隆方法,但是某一个对象直接调用clone方法,会抛出异常,即并不能成功克隆一个对象。调用该方法时,必须实现一个Cloneable 接口。这也就是原型模式的实现方式。还有即如果该类实现了cloneable接口,尽管构造函数是私有的,他也可以创建一个对象。即clone方法是不会调用构造函数的,他是直接从内存中copy内存区域的。
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值