面试官有毒吧?让实现线程安全的单例,又不让使用synchronized

单例模式,是Java中比较常见的一个设计模式,也是我在面试时常常会问到的一个问题。面试

 

通过个人初步统计,基本上有60%左右的人能够说出2-4种单例的实现方式,有40%左右的人能够说出5-6种单例的实现方式,只有20%左右的人可以说出7种单例的实现。算法

 

而只有不到1%的人可以说出7种以上的单例实现。设计模式

 

其实,做为面试官,我大多数状况下之因此问单例模式,是由于这个题目能够问到不少知识点。安全

 

好比线程安全、类加载机制、synchronized的原理、volatile的原理、指令重排与内存屏障、枚举的实现、反射与单例模式、序列化如何破坏单例、CAS、CAS的ABA问题、Threadlocal等知识。微信

 

通常状况下,只须要从单例开始问起,大概就能够完成一场面试的整个流程,把我想问的东西都问完,能够比较全面的了解一个面试者的水平。多线程

 

如下,是一次面试现场的还原,从单例模式开始:模块化

 

Q:你知道怎么不使用synchronized和lock实现一个线程安全的单例吗?学习

A:我知道,可使用"静态内部类"实现。线程

 

静态内部类实现单例模式:设计

 

面试官有毒吧?让实现线程安全的单例,又不让使用synchronized

 

Q:除了静态内部类还会其余的方式吗?

A:还有就是两种饿汉模式。

 

饿汉实现单例模式:

 

面试官有毒吧?让实现线程安全的单例,又不让使用synchronized

 

 

饿汉变种实现单例模式:

 

面试官有毒吧?让实现线程安全的单例,又不让使用synchronized

 

Q:那你上面提到的几种都是线程安全的吗?

A:是线程安全的

Q:那是如何作到线程安全的呢?

A:应该是由于我使用了static,而后类加载的时候就线程安全了吧?

Q:其实你说的并不彻底对,由于以上几种虽然没有直接使用synchronized,可是也是间接用到了。

(这里面根据回答状况会朝两个不一样的方向展开:一、类加载机制、模块化等;二、继续深刻问单例模式)

 

类加载过程的线程安全性保证

以上的静态内部类、饿汉等模式均是经过定义静态的成员变量,以保证单例对象能够在类初始化的过程当中被实例化。

 

这实际上是利用了ClassLoader的线程安全机制。ClassLoader的loadClass方法在加载类的时候使用了synchronized关键字。

因此, 除非被重写,这个方法默认在整个装载过程当中都是线程安全的。因此在类加载过程当中对象的建立也是线程安全的。

 

Q:那还回到刚开始的问题,你知道怎么不使用synchronized和lock实现一个线程安全的单例吗?

(并非故意穷追不舍,而是但愿能能够引起面试者的更多思考)

A:额、、、那枚举吧,枚举也能够实现单例。

 

枚举实现单例模式:

 

面试官有毒吧?让实现线程安全的单例,又不让使用synchronized

 

Q:那你知道枚举单例的原理吗?如何保证线程安全的呢?

 

枚举单例的线程安全问题

枚举其实底层是依赖Enum类实现的,这个类的成员变量都是static类型的,而且在静态代码块中实例化的,和饿汉有点像, 因此他自然是线程安全的。

 

Q:因此,枚举其实也是借助了synchronized的,那你知道哪一种方式能够彻底不使用synchronized的吗?

A:en....我想一想

Q:(过了一会他好像没有思路)你知道CAS吗?使用CAS能够实现单例吗?

(面试中,若是面试者对于锁比较了解的话,那我大多数状况下都会继续朝两个方向深刻问:一、锁的实现原理;二、非锁,如CAS、ThreadLocal等)

A:哦,我知道,CAS是一项乐观锁技术,当多个线程尝试使用CAS同时更新一个变量时,只有其中一个线程能更新成功。

 

借助CAS(AtomicReference)实现单例模式:

public class Singleton {

private static final AtomicReference<Singleton> INSTANCE = new AtomicReference<Singleton>();

private Singleton() {}

public static Singleton getInstance() {

for (;;) {

Singleton singleton = INSTANCE.get();

if (null != singleton) {

return singleton;

}

singleton = new Singleton();

if (INSTANCE.compareAndSet(null, singleton)) {

return singleton;

}

}

}

}

Q:使用CAS实现的单例有没有什么优缺点呀?

A:用CAS的好处在于不须要使用传统的锁机制来保证线程安全,CAS是一种基于忙等待的算法,依赖底层硬件的实现,相对于锁它没有线程切换和阻塞的额外消耗,能够支持较大的并行度。

Q:你说的好像是优势?那缺点呢?

 

CAS实现的单例的缺点

CAS的一个重要缺点在于若是忙等待一直执行不成功(一直在死循环中),会对CPU形成较大的执行开销。

 

另外,代码中,若是N个线程同时执行到 singleton = new Singleton();的时候,会有大量对象被建立,可能致使内存溢出。

Q:好的,除了使用CAS之外,你还知道有什么办法能够不使用synchronized实现单例吗?

A:这回真的不太知道了。

Q:(那我再提醒他一下吧)能够考虑下ThreadLocal,看看能不能实现?

(面试者没有思路的时候,我几乎都会先作一下提醒,实在没有思路再换下一个问题)

A:ThreadLocal?这也能够吗?

Q:你先说下你理解的ThreadLocal是什么吧

(经过他的回答,貌似对这个思路有些疑惑,不着急。先问一个简单的问题,让面试者放松一下,找找自信,而后再继续问)

 

ThreadLoacal

ThreadLocal会为每个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。对于多线程资源共享的问题,同步机制(synchronized)采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。

 

同步机制仅提供一份变量,让不一样的线程排队访问,而ThreadLocal为每个线程都提供了一份变量,所以能够同时访问而互不影响。

Q:那理论上是否是可使用ThreadLocal来实现单例呢?

A:应该也是可行的。

 

使用ThreadLocal实现单例模式:

 

面试官有毒吧?让实现线程安全的单例,又不让使用synchronized

 

Q:嗯嗯,好的,那有关单例模式的实现的问题我就问的差很少了。

(ThreadLocal这种写法主要是考察面试者对于ThreadLocal的理解,以及是否能够把知识活学活用,可是实际上,这种所谓的"单例",其实失去了单例的意义...)

(可是说实话,能回答到这一题的人不多,大多数面试者基本上在前面几道题就已经没有思路了,大多数状况下根本不会问到这个问题就要改方向了)

A:(心中窃喜)嗯嗯,学习到不少,感谢

Q:那...你知道如何破坏单例吗?

(单例问题,必问的一个。经过这个引伸到序列化和反射的相关知识)

A:(额....)

最后这里小编整理了一套面试资料,让你面试不慌张

 

面试官有毒吧?让实现线程安全的单例,又不让使用synchronized

 

面试官有毒吧?让实现线程安全的单例,又不让使用synchronized

 

面试官有毒吧?让实现线程安全的单例,又不让使用synchronized

 

领取步骤:
一、加微信便可免费领取

相关文章
相关标签/搜索