小白算法_楔子

前言

笔者属于算法小白一枚,本系列文章属于算法的学习笔记,也希望能给算法小小白起到些许的指引作用。如果有算法大佬不小心点了进来,只能说一声抱歉打扰了。

思考

作为本系列文章的楔子,我们今天不讨论算法解题,而是来谈谈一个老朋友:单例模式。相信大家对单例是非常熟悉的,但是如果要让你手写单例呢?写起来磕磕碰碰还是娴熟流畅?你会写几种?分别有什么区别?现在大家都在往 kotlin 转,那么在 kotlin 里面使用 object 关键字实现的单例又采用的是哪种实现方式呢?笔者从事 android 开发多年,在几个互联网大厂的面试中,经历了两次手写单例的考验,所以在这里以手写单例为楔子,为后面的手写算法起一个抛砖引玉的作用。

正文

什么是单例模式

在同一个进程中,有些时候我们需要某个类同时只保留一个对象,这个时候就应该考虑单例模式的设计。

单例模式的特点

  • 单例模式只能有一个实例对象
  • 单例模式必须创建自己的唯一实例
  • 单例模式必须对外部提供这一实例

单例模式的实现

饿汉式

public class Singleton {

    private static Singleton INSTANCE = new Singleton();

    private Singleton(){
    }

    public static Singleton getInstance(){
        return INSTANCE;
    }
}

优点:线程安全,使用没有延迟。
缺点:类加载即初始化实例,内存浪费。

面试官追问:为什么线程安全?

  1. 饿汉式本质上是使用的是静态变量。
  2. 在类加载的过程中,静态变量就会进行初始化。
  3. 静态变量只会初始化一次,并且被所有的对象所共享。

懒汉式

public class Singleton {

    private volatile static Singleton INSTANCE = null;

    private Singleton() {
    }

    public static Singleton getInstance() {
        if (INSTANCE == null) {
            synchronized (Singleton.class) {
                if (INSTANCE == null) {
                    INSTANCE = new Singleton();
                }
            }
        }
        return INSTANCE;
    }
}

优点:懒加载节省资源,线程安全。
缺点:线程同步存在性能损耗

面试官追问:为什么采用双重锁检验?为什么使用 volatile 关键字?

  1. 线程同步是存在性能损耗的,我们只需要在实际创建对象的时候进行同步,而不需要同步多余的代码。
  2. 第一重校验不为 null 的时候,直接返回对象。否则准备创建对象,此时需要进行线程同步。
  3. 在创建对象之前还需要进行第二重校验,是为了确保等待同步的线程不会重复创建对象。
  4. 使用 volatile 关键字是为了禁止指令重排,确保对象初始化完全。

静态内部类

public class Singleton {

    private Singleton() {
    }

    private static class SingletonHolder {
        private static Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

优点:懒加载节省资源,无性能损耗的线程安全

面试官追问:如何实现的懒加载?为什么线程安全

  1. 静态内部类只有被调用的时候才会加载,满足懒加载。
  2. 静态内部类在加载的时候,jvm 会保证线程安全。

总结

单例模式的实现很多,我们这里只取精华,只讲重点。最关键的是在面试过程中,你是否可以在纯文本编辑的环境下娴熟的手写以上几种不同的单例,然后从容的告诉面试官它们之间的区别,最后再承受住面试官深挖的几个为什么。

借一句古语,与诸君共勉:纸上得来终觉浅,绝知此事要躬行。

一个小小的单例提醒了我们,在后面的算法学习中要脚踏实地的手写算法解题,切记不可只做头脑风暴。在大厂的高级职位面试过程中,面试官最喜欢做的就是让求职者手写点代码。不需要太多,也不需要太难,就已经可以看出很多东西了。两位求职者,一个手写起来娴熟流畅,代码强壮,格式工整。另外一个先不说测试用例是否通过,写的过程都磕磕碰碰。你是面试官,你会如何抉择呢?

最后附上 kotlin 通过 object 关键字实现单例的字节码反编译出来的 java 源代码,跟你想的是否一样呢?

public final class Singleton {
   public static final Singleton INSTANCE;

   private Singleton() {
   }

   static {
      Singleton var0 = new Singleton();
      INSTANCE = var0;
   }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。