经典算法题: 实现Singleton单例模式
Posted 程外城
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了经典算法题: 实现Singleton单例模式相关的知识,希望对你有一定的参考价值。
渣硕笔记
剑指 offer 经典题系列
面试题2: 实现单例模式。即设计一个类,任何时候我们都只能生成该类的一个实例。
题目解析
单例模式是常见常考的设计模式,它的实现的方法也有多种,本文将对各种方法进行介绍。
代码最优解
推荐解法一
双重校验加锁
1public class Singleton {
2 private volatile static Singleton instance = null;
3
4 private Singleton() {}
5
6 public static Singleton getInstance() {
7 // 先检查实例是否存在,如果不存在才进入下面的同步块
8 if (instance == null) {
9 // 同步块,线程安全的创建实例
10 synchronized (Singleton.class) {
11 // 再次检查实例是否存在,如果不存在,才真正地创建实例
12 if (instance == null) {
13 instance = new Singleton();
14 }
15 }
16 }
17 return instance;
18 }
19}
代码理解
之所以这段代码叫做双重校验加锁,就是因为使用了内外两层if条件判断,并在两次判断中间加锁。下面具体解析:
(1). 第一层校验(外层判断是否为null)的作用 ?
它的作用是避免每次进来都要加锁或者等待锁。当我们的实例化一个单例之后,其他后续的所有请求都没必要在进入同步代码块继续往下执行了,直接返回我们曾生成的实例即可,只有实例还未创建时才进入同步代码块。
(2). 第二层校验(内层判断是否为null)的作用 ?
它的作用是保证线程安全。假设我们去掉第二层校验,会出现以下错误情况:A线程和B线程都在同步块外面判断了instance为null,结果t1线程首先获得了线程锁,进入了同步块,然后t1线程会创造一个实例,此时instance已经被赋予了实例,t1线程退出同步块,直接返回了第一个创造的实例,此时t2线程获得线程锁,也进入同步块,此时t1线程其实已经创造好了实例,t2线程正常情况应该直接返回的,但是因为同步块里没有判断是否为null,直接就是一条创建实例的语句,所以t2线程也会创造一个实例返回,此时就造成创造了多个实例的情况。
(3). 为什么变量修饰为volatile ?
虚拟机在执行创建实例的这一步操作的时候,其实是分了好几步去进行的,也就是说创建一个新的对象并非是原子性操作,在多线程的场景下可能会由于指令重排序造成错误。因此,需要使用volatile保证每次读取变量值,都直接从内存中读取,避免重排序影响。
(4). 为什么变量和方法都需要定义为static ?
static关键字则用于定义静态变量和静态方法。
静态变量被所有的对象所共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。
静态方法不依赖于任何对象就可以进行访问,方法被类所拥有。另外,值得注意的,非静态方法无法访问静态变量。
推荐解法二
静态内部类
1public class SingletonDemo {
2 private static class SingletonHolder{
3 private static SingletonDemo instance=new SingletonDemo();
4 }
5 private SingletonDemo(){
6 private static SingleTon INSTANCE = new SingleTon();
7 }
8 public static SingletonDemo getInstance(){
9 return SingletonHolder.instance;
10 }
11}
代码理解
使用静态内部类的好处是:静态内部类不会在类加载时就加载,而是在调用getInstance()方法时才进行加载,达到了类似懒汉模式的效果,而这种方法又是线程安全的。
静态内部类的优点是:外部类加载时并不需要立即加载内部类,内部类不被加载则不去初始化INSTANCE,故而不占内存。即当SingleTon第一次被加载时,并不需要去加载SingleTonHoler,只有当getInstance()方法第一次被调用时,才会去初始化INSTANCE,第一次调用getInstance()方法会导致虚拟机加载SingleTonHoler类,这种方法不仅能确保线程安全,也能保证单例的唯一性,同时也延迟了单例的实例化。
写在最后
单例模式题目除了写代码之后,可能在面试中还会考察对单例模式的理解,后续将进行相关内容介绍。
以上是关于经典算法题: 实现Singleton单例模式的主要内容,如果未能解决你的问题,请参考以下文章