【面试题:手写单例模式】

面试题:手写单例模式

  • 介绍
  • 几种常见的实现方式
    • 饿汉模式
      • 原理
      • 优点
      • 缺点
      • 代码展示
      • 截图验证
    • 懒汉模式
      • 原理
      • 优点
      • 缺点
      • 代码
    • 枚举模式
      • 原理
      • 优点
      • 缺点
      • 代码
      • 测试验证
  • 总结

介绍

java 面试题中,手写单例模式 是一个命中率比较高的面试题。因此,我们必须得熟练掌握它。其实它并不难,就看你有没有掌握其中的原理。 其实在我刚接触java 编程的时候,我更多的是死记硬背这个单例模式。每次面试的时候都要背一遍。现在想起来,很是尴尬。如果有对这个单例模式还有不清楚的地方,希望本博客能帮助到大家。

几种常见的实现方式

饿汉模式

原理

因为在类加载完成的时候就实现了初始化,是虚拟机完成的,根本不存在多线程的问题。此时都还没有多线程竞争现象。

优点

代码编写简单,比较好理解。速度快。

缺点

如果是比较大的对象,会从类加载的时候一直占据服务器内存。可能这个对象在服务器运行很长时间后才会需要创建。那么这段时间就是白白占据着内存。并且反射 可以暴力破解 私有构造方法。

代码展示

/**
 * @author :echo_黄诗
 * @description:饿汉模式
 * @date :2023/2/6 16:40
 */
public class Hungry {
    //静态熟悉,类加载时初始化,避免多线程初始化的问题
    private static  Hungry hungry=new Hungry();

    //私有构造
    private Hungry(){}

    //公共方法获取实例
    public static Hungry getHungryEntity(){
        return hungry;
    }

    /**
     * 50 个线程模拟 多线程环境
     * @param args
     */
    public static void main(String[] args) {
        for (int i=0;i<50;i++){
            Thread thread=new Thread(){
                @Override
                public void run() {
                    System.out.println("获取单例实例: "+getHungryEntity());
                }
            };
            thread.start();
        }
    }
}

截图验证

【面试题:手写单例模式】_第1张图片

懒汉模式

原理

在客户端调用的时候才初始化。采用同步代码块的方式,保证只初始化一个实例

优点

和饿汉模式相比,它只有在调用的时候才会被加载。服务器的性能会相对的有所提升。

缺点

代码稍微优点复杂,对程序员的要求有所提高,对类加载和并发知识有要求。并且反射 可以暴力破解 私有构造方法。

代码

/**
 * @author :echo_黄诗
 * @description:懒汉模式  调用的时候才会初始化
 * @date :2023/2/6 16:45
 */
public class LazySingle {

    private static Object lock=new Object();
    private LazySingle() {
    }

    //禁止指令重排
    private volatile static LazySingle lazySingle = null;

    public static LazySingle getSingleInstance() {
        if (lazySingle == null) {
            //此时有多个线程进来 ,比如线程A B
            synchronized (lock) {
                //此时lazySingle 可能已经被初始化了,需要重新判断下
                // 如果A 线程进来初始化了,B 进来后就不需要初始化了,所以需要再次判断
                //  使用 volatile 关键字原因 初始化步骤不是原子性的。
                // 分为三步 第一步 分配内存空间
                //         第二步 初始化
                //         第三步 将内存首地址赋值给变量
                //  比如第三步和第一步完成了,第二步比较耗时,还没有完成。 
                //  lazySingle == null  这个判断为true. 此时直接返回对象,返回的是一个不完全的对象。
                // volatile 关键字保证,第三步完成之前,第二步必须要完成,不能指令重排
                if (lazySingle == null) {
                    lazySingle = new LazySingle();
                }
            }
        }
        return lazySingle;
    }

    /**
     * 测试
     * @param args
     */
    public static void main(String[] args) {
        for (int i=0;i<50;i++){
            Thread thread=new Thread(){
                @Override
                public void run() {
                    System.out.println("获取单例实例: "+getSingleInstance());
                }
            };
            thread.start();
        }
    }
}

【面试题:手写单例模式】_第2张图片

枚举模式

Google 首席 Java 架构师、《Effective Java》一书作者、Java集合框架的开创者Joshua Bloch在Effective Java一书中提到:
单元素的枚举类型已经成为实现Singleton的最佳方法。

原理

Java虚拟机会保证枚举对象的唯一性,因此每一个枚举类型和定义的枚举变量在JVM中都是唯一的。 由虚拟机保证。

优点

既可以避免多线程同步问题;还可以防止通过反射和反序列化来重新创建新的对象。在很多优秀的开源代码中,我们经常可以看到使用枚举方式来实现的单例模式类。

缺点

可能对枚举不熟悉是它的缺点了。

代码

/**
 * @author :echo_黄诗
 * @description:枚举实现 单例
 * @date :2023/2/6 17:11
 */
public class EnumSingleton {
    private EnumSingleton(){}

    static enum SingletonEnum{
        //创建一个枚举对象,为单例对象
        INSTANCE;
        private EnumSingleton enumSingleton;
        private SingletonEnum(){
            enumSingleton=new EnumSingleton();
        }

        public EnumSingleton getInstance(){
            return enumSingleton;
        }
    }

    public static EnumSingleton getInstance(){
        return SingletonEnum.INSTANCE.getInstance();
    }

    public static void main(String[] args) {
        for (int i=0;i<50;i++){
            Thread thread=new Thread(){
                @Override
                public void run() {
                    System.out.println("获取单例实例: "+getInstance());
                }
            };
            thread.start();
        }
    }
}

测试验证

【面试题:手写单例模式】_第3张图片

总结

简单介绍了三种实现方式。饿汉模式,懒汉模式和枚举方式。建议使用 枚举方式。如果大家有什么建议,欢迎讨论。

你可能感兴趣的:(多线程编程,设计模式,单例模式,java,多线程)