DCL之单例模式

所谓的DCL 就是 Double Check Lock,即双重锁定检查,在了解DCL在单例模式中如何应用之前,我们先了解一下单例模式。单例模式通常分为“饿汉”和“懒汉”,先从简单入手

饿汉

所谓的“饿汉”是因为程序刚启动时就创建了实例,通俗点说就是刚上菜,大家还没有开始吃的时候就先自己吃一口。

public class Singleton { private static final Singleton singleton = new Singleton(); private Singleton(){} public static Singleton getInstance(){ return singleton; } }

第3行 通过一个私有构造方法限制了创建此类对象的途径(反射忽略)。这种方法很安全,但从某种程度上有点浪费资源,比方说从一开始就创建了Singleton实例,但很少去用它,这就造成了方法区资源的浪费,因此出现了另外一种单例模式,即懒汉单例模式

懒汉

之所以叫“懒汉”是因为只有真正叫它的时候,才会出现,不叫它它就不理,跟它没关系。也就是说真正用到它的时候才去创建实例,并不是一开始就创建实例。如下代码所示:

public class Singleton { private static Singleton singleton = null; private Singleton(){} public static Singleton getInstance(){ if(null == singleton){ singleton = new Singleton(); } return singleton; } }

看似很简单的一段代码,但存在一个问题,就是线程不安全的问题。例如,现在有1000个线程,都需要这一个Singleton的实例,验证一下是否拿到同一个实例,代码如下所示:

public class Singleton { private static Singleton singleton = null; private Singleton(){} public static Singleton getInstance(){ if(null == singleton){ try { Thread.sleep(1);//象征性的睡了1ms } catch (InterruptedException e) { e.printStackTrace(); } singleton = new Singleton(); } return singleton; } public static void main(String[] args) { for (int i=0;i<1000;i++){ new Thread(()-> System.out.println(Singleton.getInstance().hashCode())).start(); } } }

部分运行结果,乱七八糟:

944436457 1638599176 710946821 67862359

为什么会这样?第一个线程过来了,执行到第7行,睡了1ms,正在睡的同时第二个线程来了,第二个线程执行到第5行时,结果肯定为空,因此接下来将会有两个线程各自创建一个对象,这必然会导致Singleton.getInstance().hashCode()结果不一致。可以通过给整个方法加上一把锁改进如下:

改进1 public class Singleton { private static Singleton singleton = null; private Singleton(){} public static synchronized Singleton getInstance(){ if(null == singleton){ try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } singleton = new Singleton(); } return singleton; } public static void main(String[] args) { for (int i=0;i<1000;i++){ new Thread(()-> System.out.println(Singleton.getInstance().hashCode())).start(); } } }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zwyxyx.html