单例模式是用来创建只能有一个实例对象的模式,该模式虽然只有一个类,但却并没有想象中那么容易,下面我们就一起来学习下。
场景描述
单例模式的类图上只有一个类,场景很简单,就是有一个类,让它只能实例化。
代码示例
下面会列出几个版本的代码示例,其中有的版本是有问题的,最终逐步会实现成最终正确的版本。
第一个有问题的版本
|
|
该版本的实现有一个隐藏的问题,就是当多个线程同时获取实例的时候,有可能会创建出多个实例。因为getInstance()方法并没有加锁,当两个线程同时跑到uniqueInstance == null
时,两者都判断为空,就会创建出两个不一样的实例。
加同步锁的版本
|
|
这个版本只是简单地在getInstance()
方法上添加了synchronized,来限制同时只有一个线程访问该方法。虽然同步版本解决了可能会创建多个实例的问题,但同时带来了额外的锁性能开销。更严重的是,其实只有第一次执行此方法时,才需要同步,之后每次调用,同步都是累赘。
预实例化版本
|
|
这个版本也是简单粗暴,不管用不用,在JVM加载这个类时马上实例化了一个对象。JVM保证在任何线程访问uniqueInstance
之前,一定已经实例化了。这个版本的缺点就是放弃了延迟初始化。
双重检查加锁版本
|
|
这个版本利用了volatile多线程之间的可见性,以及局部代码加锁,解决了性能问题,是目前最优的单例模式版本了。但是还能再进一步,我们往下看。
静态内部类方式
|
|
利用静态内部类机制来初始化实例,静态内部类只有当被调用的时候才开始被JVM加载,而且JVM的加载保证了线程安全。
上面实现的四种正确的版本中,个人偏向于最后一种。因为有延迟加载,线程安全且没有锁开销,代码实现也比较优雅。