Java——》谈谈你对单例模式的理解

一、单例模式的作用

单例模式的核心是保证一个类只有一个实例,并且提供一个访问实例的全局访问点。

二、单例模式的实现

实现方式优缺点
饿汉式线程安全,调用效率高 ,但是不能延迟加载,可能造成空间资源浪费
懒汉式:锁式线程安全,调用效率不高synchronized,能延迟加载
懒汉式:双重检测锁式在懒汉式的基础上解决并发问题
静态内部类式线程安全,资源利用率高,可以延时加载
枚举单例线程安全,调用效率高,但是不能延迟加载

1、饿汉式


/**
 * 单例模式:饿汉式
 */
public class SingletonForHungry {

    // 缺点:如果对象没有被使用,造成空间资源浪费
    private byte[] b1 = new byte[1024 * 1024];
    private byte[] b2 = new byte[1024 * 1024];
    private byte[] b3 = new byte[1024 * 1024];

    // 声明此类型的变量,并实例化,当该类被加载的时候就完成了实例化并保存在了内存中
    private static final SingletonForHungry singletonInstance = new SingletonForHungry();

    // 私有化所有的构造方法,防止直接通过new关键字实例化
    private SingletonForHungry() {
    }

    // 对外提供一个获取实例的静态方法
    public static SingletonForHungry getSingletonInstance() {
        return singletonInstance;
    }

    public static void main(String[] args) {
        System.out.println(SingletonForHungry.singletonInstance);
        System.out.println(SingletonForHungry.singletonInstance);
    }
}

2、懒汉式(锁式)

/**
 * 单例模式:懒汉式(锁式)
 */
public class SingletonForLazySynchronized {
    // 声明此类型的变量,但没有实例化
    private static SingletonForLazySynchronized singletonInstance;

    // 私有化所有的构造方法,防止直接通过new关键字实例化
    private SingletonForLazySynchronized() {

    }

    // 对外提供一个获取实例的静态方法,为了数据安全添加synchronized关键字
    public static SingletonForLazySynchronized getSingletonInstance() {
        if (singletonInstance == null) {
            singletonInstance = new SingletonForLazySynchronized();
        }
        return singletonInstance;
    }

    public static void main(String[] args) {

        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                System.out.println(SingletonForLazySynchronized.getSingletonInstance());
            }).start();
        }
    }
}

3、懒汉式(双重检测锁式)

/**
 * 单例模式:懒汉式(双重检测锁式)
 */
public class SingletonForLazyDoubleCheck {

    // 声明此类型的变量,但没有实例化
    // 必须加volatile,防止指令重排序
    private static volatile SingletonForLazyDoubleCheck singletonInstance;

    // 私有化所有的构造方法,防止直接通过new关键字实例化
    private SingletonForLazyDoubleCheck() {
    }

    // 对外提供一个获取实例的静态方法
    public static SingletonForLazyDoubleCheck getSingletonInstance() {
        if (singletonInstance == null) {
            synchronized (SingletonForLazyDoubleCheck.class) {
                // 双重检测
                if (singletonInstance == null) {
                    // 加volatile防止指令重排序
                    // 1.分配内存空间
                    // 2.创建实例对象
                    // 3.把这个内存地址赋值给变量的引用
                    singletonInstance = new SingletonForLazyDoubleCheck();
                }
            }
        }
        return singletonInstance;
    }

    public static void main(String[] args) throws Exception {
        System.out.println(SingletonForLazyDoubleCheck.getSingletonInstance());
        System.out.println(SingletonForLazyDoubleCheck.getSingletonInstance());
    }

}

4、静态内部类式

/**
 * 单例模式:静态内部类
 */
public class SingletonForStaticInner {

    // 静态内部类
    public static class SingletonClassInstance {
        // 声明外部类型的静态常量
        public static final SingletonForStaticInner singletonInstance = new SingletonForStaticInner();
    }

    // 私有化构造方法
    private SingletonForStaticInner() {

    }

    // 对外提供的唯一获取实例的方法
    public static SingletonForStaticInner getSinletonInstance() {
        return SingletonClassInstance.singletonInstance;
    }

    public static void main(String[] args) {
        System.out.println(SingletonForStaticInner.getSinletonInstance());
        System.out.println(SingletonForStaticInner.getSinletonInstance());
    }
}

5、枚举单例

/**
 * 单例模式:枚举
 */
public enum SingletonForEnum {
    INSTANCE;

    public static void main(String[] args) {
        System.out.println(SingletonForEnum.INSTANCE);
        System.out.println(SingletonForEnum.INSTANCE);
    }
}

三、单例模式的破解

1、反射破坏单例模式

反射是可以操作私有的属性和方法,通过私有构造器创建实例

package com.example.test.singleton;

import java.lang.reflect.Constructor;

/**
 * 单例模式:懒汉式(双重检测锁式)
 */
public class SingletonForLazyDoubleCheck {

    // 声明此类型的变量,但没有实例化
    // 必须加volatile,防止指令重排序
    private static volatile SingletonForLazyDoubleCheck singletonInstance;


    // 私有化所有的构造方法,防止直接通过new关键字实例化
    private SingletonForLazyDoubleCheck() {
    }

    // 对外提供一个获取实例的静态方法
    public static SingletonForLazyDoubleCheck getSingletonInstance() {
        if (singletonInstance == null) {
            synchronized (SingletonForLazyDoubleCheck.class) {
                // 双重检测
                if (singletonInstance == null) {
                    // 加volatile防止指令重排序
                    // 1.分配内存空间
                    // 2.创建实例对象
                    // 3.把这个内存地址赋值给变量的引用
                    singletonInstance = new SingletonForLazyDoubleCheck();
                }
            }
        }
        return singletonInstance;
    }

    public static void destorySigleton() throws Exception {
        // 反射可以暴力破解
        Class<SingletonForLazyDoubleCheck> cls = SingletonForLazyDoubleCheck.class;
        SingletonForLazyDoubleCheck singleton = cls.newInstance();
        System.out.println("反射获取实例对象 = " + singleton);

        Constructor<?>[] declaredConstructors = cls.getDeclaredConstructors();
        declaredConstructors = cls.getDeclaredConstructors();
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            declaredConstructor.setAccessible(true);
            Object o = declaredConstructor.newInstance(null);
            System.out.println("反射获取实例对象 = " + o);
        }
    }

    public static void main(String[] args) throws Exception {
        destorySigleton();
    }

}

在这里插入图片描述

2、反射破坏单例模式的解决方案

解决的方式是在无参构造方法中手动抛出异常控制,或者声明一个全局变量来控制。

package com.example.test.singleton;

import java.lang.reflect.Constructor;

/**
 * 单例模式:懒汉式(双重检测锁式)
 */
public class SingletonForLazyDoubleCheck {

    // 声明此类型的变量,但没有实例化
    // 必须加volatile,防止指令重排序
    private static volatile SingletonForLazyDoubleCheck singletonInstance;

    private static boolean flag = false; // false 表示对象还没有被创建


    // 私有化所有的构造方法,防止直接通过new关键字实例化
    private SingletonForLazyDoubleCheck() {
        if(!flag){
            // 说明是第一次创建
            flag = true;
            singletonInstance = this;
        }else{
            throw  new RuntimeException("已经有了对象,就不要再创建了...");
        }
    }

    // 对外提供一个获取实例的静态方法
    public static SingletonForLazyDoubleCheck getSingletonInstance() {
        if (singletonInstance == null) {
            synchronized (SingletonForLazyDoubleCheck.class) {
                // 双重检测
                if (singletonInstance == null) {
                    // 加volatile防止指令重排序
                    // 1.分配内存空间
                    // 2.创建实例对象
                    // 3.把这个内存地址赋值给变量的引用
                    singletonInstance = new SingletonForLazyDoubleCheck();
                }
            }
        }
        return singletonInstance;
    }

    public static void destorySigleton() throws Exception {
        // 反射可以暴力破解
        Class<SingletonForLazyDoubleCheck> cls = SingletonForLazyDoubleCheck.class;
        SingletonForLazyDoubleCheck singleton = cls.newInstance();
        System.out.println("反射获取实例对象 = " + singleton);

        Constructor<?>[] declaredConstructors = cls.getDeclaredConstructors();
        declaredConstructors = cls.getDeclaredConstructors();
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            declaredConstructor.setAccessible(true);
            Object o = declaredConstructor.newInstance(null);
            System.out.println("反射获取实例对象 = " + o);
        }
    }

    public static void main(String[] args) throws Exception {
        destorySigleton();
    }

}

四、哪些框架(场景)中用到了单例模式的设计

1、Spring中的Bean对象(默认是单例模式)
2、相关的工厂对象,比如:MyBatis中的SqlSessionFactory,Spring中的BeanFactory
3、相关的配置对象,比如:MyBatis中的Configuration对象,SpringBoot中的各个XXAutoConfiguration对象
4、应用程序的日志框架
5、数据库连接池

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值