atwood-pan

 

02-设计模式——单例模式

设计模式——单例模式

通过

javap -v -p xxxx.class

可以查看对应字节码信息

模式定义

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

场景:

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

懒汉模式

1.懒汉模式:延迟加载,只有在真正使用的时候,才开始实例化

  • 线程安全问题
  • double check 双重检查,加锁优化
  • 编译器(JIT),CPU有可能对指令进行重排序,导致使用到尚未初始化的实例,可以通过添加volatile关键字进行修饰,对于volatile修饰的字段,可以防止指令重排
/**
 * @program: DesignPatterns
 * @description: 懒汉单例模式
 * @author: Coder_Pan
 * @create: 2022-04-12 18:48
 **/
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();
        /**
         * 单个线程下一切ok
         */
//        LazySingleton instance = LazySingleton.getInstance();
//        LazySingleton instance1 = LazySingleton.getInstance();
//        System.out.println(instance == instance1);

    }
}

/**
 * 创建懒汉实例
 */
class LazySingleton {
    /**
     * 加上volatile防止重排序,规定字节码层面顺序为123
     */
    private volatile  static LazySingleton instance;
    private LazySingleton(){

    }

    public  static LazySingleton getInstance() {
        /**
         * 如果实例为空,才创建
         * synchronized  =》 加锁,保证只有一个实例
         */
        if (instance == null) {
            /**
             * 当对象为空时才进行加锁
             *
             * 防止重复加锁操作
             */
            synchronized (LazySingleton.class){
                if (instance == null) {
                    instance = new LazySingleton();
                    //字节码层
                    //1.分配空间
                    //2.初始化
                    //3.引用赋值
                }
            }
        }
        return instance;
    }
//    public synchronized static LazySingleton getInstance() {
//        /**
//         * 如果实例为空,才创建
//         * 需要在LazySingleton的getInstance上加上synchronized才能保持多个线程下实例对象的一致性,这个方式会造成资源浪费,重复加锁
//         * synchronized  =》 加锁,保证只有一个实例
//         */
//        if (instance == null) {
//
//            instance = new LazySingleton();
//        }
//        return instance;va
//    }
}

饿汉模式

类加载的初始化阶段就完成了实例的初始化

本质上就是借助与jvm类加载机制,保证实例的唯一性

类加载过程:

  1. 加载二进制数据到内存中,生成对应的Class数据结构
  2. 连接:a、验证 ,b、准备(给类的静态成员变量赋默认值),c、解析
  3. 初始化:给类的静态变量赋初值

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

/**
 * @program: DesignPatterns
 * @description: 饿汉单例模式
 * @author: Coder_Pan
 * @create: 2022-04-12 19:32
 **/
public class HungrySingletonTest {
    public static void main(String[] args) {
        System.out.println("饿汉单例模式...HungrySingleton");
        HungrySingleton instance = HungrySingleton.getInstance();
        HungrySingleton instance1 = HungrySingleton.getInstance();
        System.out.println(instance == instance1);
    }
}

/**
 * 饿汉模式 ⬇
 */
 class HungrySingleton{
     /**
      * 静态属性在何时实例化
      */
    private static HungrySingleton instance = new HungrySingleton();

    private HungrySingleton(){

    }

     /**
      * 公开的方法,对外提供访问
      * @return
      */
    public static HungrySingleton getInstance() {
        return instance;
    }
 }

静态内部类

  • 本质上是利用类的加载机制来保证线程安全
  • 只有在实际使用的时候,才会触发类的初始化,所有页式懒加载的一种形式
/**
 * @program: DesignPatterns
 * @description: 静态内部类实现方式
 * @author: Coder_Pan
 * @create: 2022-04-12 19:56
 **/
public class InnerClassSingletonTest {
    public static void main(String[] args) {
        InnerClassSingleton instance = InnerClassSingleton.getInstance();
        InnerClassSingleton instance1 = InnerClassSingleton.getInstance();
        System.out.println(instance == instance1);
        //创建线程
        new Thread( () -> {
            InnerClassSingleton instance2 = InnerClassSingleton.getInstance();
            System.out.println("Thread - 1" + instance2);
        }).start();
        new Thread( () -> {
            InnerClassSingleton instance2 = InnerClassSingleton.getInstance();
            System.out.println("Thread - 2" + instance2);
        }).start();
    }
}


class InnerClassSingleton{
    /**
     * InnerClassHolder  =>  静态内部类
     * 基于jvm实现的懒加载模式
     *
     * 只有在调用getInstance()方法的时候才会导致静态内部类的初始化
     * 进而才会使InnerClassSingleton初始化
     */
    private static class InnerClassHolder{
        private static InnerClassSingleton instance = new InnerClassSingleton();
    }

    private InnerClassSingleton(){

    }

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

反射攻击

		 /**
         * 反射攻击	
         *
         * 通过反射创建实例,单例模式就失效了
         */
        try {
            Constructor<InnerClassSingleton> declaredConstructor = InnerClassSingleton.class.getDeclaredConstructor();
            declaredConstructor.setAccessible(true);
            InnerClassSingleton instance = declaredConstructor.newInstance();

            InnerClassSingleton instance1 = InnerClassSingleton.getInstance();
            System.out.println("反射机制创建单例模式");
            System.out.println(instance == instance1);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

class InnerClassSingleton{
    /**
     * InnerClassHolder  =>  静态内部类
     * 基于jvm实现的懒加载模式
     *
     * 只有在调用getInstance()方法的时候才会导致静态内部类的初始化
     * 进而才会使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;
    }
}

如果想实现数据的一个持久化的操作,就需要这个类进行序列化,这样jvm才会对其处理

即implements Serializable,开启序列化

源码中的应用

java.lang.Runtime
ProxyFactoryBean
DefaultSingletonBeanRegistry
ReactiveAdapterRegistry
TomcatURLStreamHandlerFactory
//反序列化指定数据源
java.util.Currency

posted on 2022-04-12 21:28  JavaCoderPan  阅读(21)  评论(0)    收藏  举报  来源

导航