单例模式,是Java中比较常见的一个设计模式,也是我在面试时经常会问到的一个问题。
经过我的初步统计,基本上有60%左右的人可以说出2-4种单例的实现方式,有40%左右的人可以说出5-6种单例的实现方式,只有20%左右的人能够说出7种单例的实现。
而只有不到1%的人能够说出7种以上的单例实现。
其实,作为面试官,我大多数情况下之所以问单例模式,是因为这个题目可以问到很多知识点。
比如线程安全、类加载机制、synchronized的原理、volatile的原理、指令重排与内存屏障、枚举的实现、反射与单例模式、序列化如何破坏单例、CAS、CAS的ABA问题、Threadlocal等知识。
一般情况下,只需要从单例开始问起,大概就可以完成一场面试的整个流程,把我想问的东西都问完,可以比较全面的了解一个面试者的水平。
以下,是一次面试现场的还原,从单例模式开始:
Q:你知道怎么不使用synchronized和lock实现一个线程安全的单例吗?
A:我知道,可以使用"静态内部类"实现。
静态内部类实现单例模式:
Q:除了静态内部类还会其他的方式吗?
A:还有就是两种饿汉模式。
饿汉实现单例模式:
饿汉变种实现单例模式:
Q:那你上面提到的几种都是线程安全的吗?
A:是线程安全的
Q:那是如何做到线程安全的呢?
A:应该是因为我使用了static,然后类加载的时候就线程安全了吧?
Q:其实你说的并不完全对,因为以上几种虽然没有直接使用synchronized,但是也是间接用到了。
(这里面根据回答情况会朝两个不同的方向展开:1、类加载机制、模块化等;2、继续深入问单例模式)
类加载过程的线程安全性保证
以上的静态内部类、饿汉等模式均是通过定义静态的成员变量,以保证单例对象可以在类初始化的过程中被实例化。
这其实是利用了ClassLoader的线程安全机制。ClassLoader的loadClass方法在加载类的时候使用了synchronized关键字。
所以, 除非被重写,这个方法默认在整个装载过程中都是线程安全的。所以在类加载过程中对象的创建也是线程安全的。
Q:那还回到刚开始的问题,你知道怎么不使用synchronized和lock实现一个线程安全的单例吗?
(并不是故意穷追不舍,而是希望能可以引发面试者的更多思考)
A:额、、、那枚举吧,枚举也可以实现单例。
枚举实现单例模式:
</