Java面试向:脑图单例模式

单例模式,绝对是面试时被问到最多的设计模式。如果搜索网上的java单例模式,这篇文章是说得最全面和深入的:

你真的会写单例模式吗——Java实现

本文的脑图就是这篇文章的总结和精炼:百度脑图

脑图截图

单例模式概念上是很简单的:提供和限制全局唯一的对象。复杂的是现实问题和实现方法。

衡量Java单例模式的好坏有几个方面:

  • 多线程安全:多个线程调用单例方法不会产生多个对象才是安全的,才达到单例模式的目的。
  • 延迟创建对象:单例模式创建的对象可能初始化一次需要耗费很多资源,内存、IO等,因此能不创建就不创建,只在调用单例方法的时候才创建。
  • 简单易懂:有几种写法还是有些复杂和反直觉的,当然为了面试,可以背一背,研究一下背后的原理。比如volatile关键字,就值得研究一下。
  • 防反射和序列化:从安全角度考虑,防止反射创建多个对象,防止反序列化时生成另一个对象。其实只有枚举法天生具有这两个能力,其他方法都可以用同样的方式达到这个目的。

另外,《游戏设计模式》一书中对单例模式的剖析很有趣,作者建议你不用单例模式。为什么?最直接的一个原因是:单例模式本质上就是一个全局变量啊!中文版传送门

以下是几种实现代码,脑图上放代码看起来有些费劲。

先定义一个获取方法的main方法来进行一个简单的测试,目的是判断是否延迟加载。

public class DemoSingleton {
    public static void main(String[] args) {
        SingletonClass.getInstance();
    }
}

另一个测试,简单的测试一下是否是线程安全的,如果输出了两个构造函数的log那么肯定是不安全的。由于线程调度的不确定性,可能需要调整一下线程的数量,再多试几次。最好玩的是第三个方法中,去掉volatile关键字或者去掉一层判断,可以更好地理解为什么要写这么多代码来保证线程安全。

public class DemoSingleton {
    public static void main(String[] args) {
        for (int i = 0; i < 25; i++) {
            new Thread(new Runnable(){
                @Override
                public void run() {
                    try {
                        Thread.sleep(1000);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    SingletonClass.getInstance();
                }
            }).start();
        }
    }
}
  1. 定义静态字段时直接初始化:简单;线程安全;不延迟
class SingletonClass {
    private static SingletonClass instance = new SingletonClass();
    public static SingletonClass getInstance() {
        System.out.println("getInstance()");
        return instance;
    }
    private SingletonClass() {
        System.out.println("new SingletonClass");
    }
}
// 输出
// new SingletonClass
// getInstance()
  1. 静态方法中简单判断唯一性:简单;线程不安全;延迟
class SingletonClass {
    private static SingletonClass instance;
    public static SingletonClass getInstance() {
        System.out.println("getInstance()");
        if (instance == null) {
            instance = new SingletonClass();
        }
        return instance;
    }
    private SingletonClass() {
        System.out.println("new SingletonClass");
    }
}
// 输出
// getInstance()
// new SingletonClass
  1. 静态方法中判断唯一性考虑线程安全:复杂;线程安全;延迟
class SingletonClass {
    private static volatile SingletonClass instance;
    public static SingletonClass getInstance() {
        System.out.println("getInstance()");
        if (instance == null) {
            synchronized(SingletonClass.class) {
                if (instance == null) {
                    instance = new SingletonClass();
                }
            }
        }
        return instance;
    }
    private SingletonClass() {
        System.out.println("new SingletonClass");
    }
}
// 输出
// getInstance()
// new SingletonClass
  1. 静态内部类:简单;线程安全;延迟
class SingletonClass {
    private static class SingletonHolder {
        private static volatile SingletonClass instance = new SingletonClass();
    }
    public static SingletonClass getInstance() {
        System.out.println("getInstance()");
        return SingletonHolder.instance;
    }
    private SingletonClass() {
        System.out.println("new SingletonClass");
    }
}
// 输出
// getInstance()
// new SingletonClass
  1. 枚举法:诡异;线程安全;不延迟
enum SingletonClass {
    INSTANCE;
    public static SingletonClass getInstance() {
        System.out.println("getInstance()");
        return INSTANCE;
    }
    private SingletonClass() {
        System.out.println("new SingletonClass");
    }
}
// 输出
// new SingletonClass
// getInstance()

拜拜,煮面时顺溜

参考资料
http://www.tekbroaden.com/singleton-java.html
https://gpp.tkchu.me/singleton.html

你可能感兴趣的:(Java面试向:脑图单例模式)