设计模式-单例模式

1、单例设计模式(Singleton)

1.1模式定义/应用场景/类图分析

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

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

1.1.1懒汉模式
  • 延迟加载,只有真正使用的时候,才开始实例化。

  • 线程安全问题

  • double check枷锁优化

  • 编译器(JIT),CPU有可能对指令进行重排序,导致使用到未初始化的实例,可以通过添加volatile关键字进行修饰。

    注:对于volatile 修饰的字段,可以防止指令重排

    package com.singleton.v1;
    ​
    public class LazySingletonTest {
        public static void main(String[] args) {
           /* LazySingleton lazySingleton=LazySingleton.getInstance();
            LazySingleton lazySingleton1=LazySingleton.getInstance();
            System.out.println(lazySingleton==lazySingleton1);*/
            new Thread(()->{
                LazySingleton lazySingleton=LazySingleton.getInstance();
                System.out.println(lazySingleton);
            }).start();
            new Thread(()->{
                LazySingleton lazySingleton=LazySingleton.getInstance();
                System.out.println(lazySingleton);
            }).start();
        }
    }
    ​
    class LazySingleton{
        private volatile static LazySingleton instance;
        private LazySingleton() {
        }
        public  static LazySingleton getInstance() {
            if(instance==null){
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized(LazySingleton.class){
                    if(instance==null){
                        instance=new LazySingleton();
                    }
                }
            }
            return instance;
        }
    }
1.1.2饿汉模式

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

类加载过程:

  1. 加载二进制数据到内存中,生成对应的Class数据结构

  2. 链接:a.验证 b.准备(给类的静态成员变量赋默认值)c.解析

  3. 初始化:给类的静态变量赋初值

        只有在真正使用对应的类时,才会触发初始化 如( 当前类是启动类即 main函数所在类,直接进行new 操作,访问静态属性、访问静态方 法,用反射访问类,初始化一个类的子类等.)

package com.singleton.v1;
​
public class HungrySingletonTest {
    public static void main(String[] args) {
        HungrySingleton hungrySingleton=HungrySingleton.getInstance();
        HungrySingleton hungrySingleton1=HungrySingleton.getInstance();
        System.out.println(hungrySingleton==hungrySingleton1);
    }
}
​
class HungrySingleton{
    private static HungrySingleton instance=new HungrySingleton();
    private HungrySingleton(){
    }
    public static HungrySingleton getInstance() {
        return instance;
    }
}
1.1.3静态内部类
  1. 本质上是利用类的加载机制来保证线程安全

  2. 只有在实际使用的时候,才会触发类的初始化,所以也是懒加载的一种的方式

package com.singleton.v1;
​
public class InnerClassSingletonTest {
    public static void main(String[] args) {
        InnerClassSingleton innerClassSingleton=InnerClassSingleton.getInstance();
        InnerClassSingleton innerClassSingleton1=InnerClassSingleton.getInstance();
        System.out.println(innerClassSingleton==innerClassSingleton1);
    }
}
​
class InnerClassSingleton{
    private static class InnerClassHolder{
        private static InnerClassSingleton instance=new InnerClassSingleton();
    }
    private InnerClassSingleton(){
    }
    public static InnerClassSingleton getInstance() {
        return InnerClassHolder.instance;
    }
}
1.1.4.反射攻击实例

饿汉模式与静态内部类可以通过在构造函数中防止反射攻击,懒汉模式无法防止。

public class InnerClassSingletonTest {
    public static void main(String[] args) throws IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException {
        InnerClassSingleton innerClassSingleton=InnerClassSingleton.getInstance();
        InnerClassSingleton innerClassSingleton1=InnerClassSingleton.getInstance();
        System.out.println(innerClassSingleton==innerClassSingleton1);
        Constructor<InnerClassSingleton> declaredConstructor = InnerClassSingleton.class.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        InnerClassSingleton innerClassSingleton2 = declaredConstructor.newInstance();
        System.out.println(innerClassSingleton1==innerClassSingleton2);
    }
}
​
class InnerClassSingleton{
    private static class InnerClassHolder{
        private static InnerClassSingleton instance=new InnerClassSingleton();
    }
    private InnerClassSingleton(){
    }
    public static InnerClassSingleton getInstance() {
        return InnerClassHolder.instance;
    }
}
静态内部类处理反射攻击
public class InnerClassSingletonTest {
    public static void main(String[] args) throws IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException {
        InnerClassSingleton innerClassSingleton=InnerClassSingleton.getInstance();
        InnerClassSingleton innerClassSingleton1=InnerClassSingleton.getInstance();
        System.out.println(innerClassSingleton==innerClassSingleton1);
        Constructor<InnerClassSingleton> declaredConstructor = InnerClassSingleton.class.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        InnerClassSingleton innerClassSingleton2 = declaredConstructor.newInstance();
        System.out.println(innerClassSingleton1==innerClassSingleton2);
    }
}
​
class InnerClassSingleton{
    private static class InnerClassHolder{
        private static InnerClassSingleton instance=new InnerClassSingleton();
    }
    private InnerClassSingleton(){
        if(InnerClassHolder.instance!=null){
            throw new RuntimeException("单例不允许创建多个");
        }
    }
    public static InnerClassSingleton getInstance() {
        return InnerClassHolder.instance;
    }
}
1.1.5枚举类型

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

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

package com.singleton.v1;
​
public enum EnumSingleton {
    INSTANCE;
    public void print(){
        System.out.println(this.hashCode());
    }
}
​
class EnumTest{
    public static void main(String[] args) {
        EnumSingleton instance = EnumSingleton.INSTANCE;
        EnumSingleton instance1 = EnumSingleton.INSTANCE;
        System.out.println(instance==instance1);
    }
}

1.2 单例模式在Spring框架&JDK源码中的应用

//JDK
java.lang.Runtime
// Spring
org.springframework.aop.framework.ProxyFactoryBean
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry
org.springframework.core.ReactiveAdapterRegistry
// Tomcat
org.apache.catalina.webresources.TomcatURLStreamHandlerFactory
// 反序列化指定数据源
java.util.Currenc

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

weixin_40714023

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值