单例模式

单例模式可以说是最常见而短小的设计模式,这也让它经常在面试中被问到。

单例模式就是某个类只能生成一个对象。主要应有于某些确实只需要一个对象的情况,比如线程池。

首先第一步当然是使用private修饰构造方法,这样别人地方就不能用new来新建一个对象。然后在代码中添加一个静态方法,这个方法可以返回一个实例对象。那么怎么控制这个方法只能返回一个对象呢?可以在类里面声明一个以这个类为静态类型的static字段instance。这样每次调用这个方法的时候就检查这个字段是不是null,是的话就new一个并且返回,不是的话就直接返回instance(已经被新建过了)。

 

但是如果单纯这样只适用于单线程的情况下,在实际应用中要考虑多线程的情况:A线程判断instance为null就准备新建,可是还没有新建成功返回对象的引用的时候发生了线程调度,B线程也因为这个instance是null而准备开始新建。结果就是产生了两个对象。

那么在这里需要做的是使这个设计模式线程安全。

方法有3:

1,使用synchronized修饰这个方法使其自动加锁实现互斥进入。问题是效率低,如果大量被使用这个方法,会有很大开销,同时只有第一次是真正需要加锁的,但这里以后每一次都有一个没意义的加锁过程。当然这么做也是解决问题的一种方式,如果确实调用很少也不是不可以用。

2,直接在类的属性声明中新建一个。比如这么写:

1 private static SingletonClass instance = new SingletonClass();

这样做的话在JVM加载这个类的时候就会由<clinit>方法自动给他创建一个,以后连对它是否为null的判断都不用了。但是问题也很明显,这样做的话无法在真正需要的时候再新建对象,如果这个对象很大,那么它过长的生命周期会给JVM的堆带来不必要的开销。

3,双重检查模式

 1 class Singleton {
 2 
 3     private volatile static Singleton instance;
 4 
 5     private Singleton() {
 6 
 7     }
 8 
 9     public static Singleton getInstance() {
10         if (instance == null) {
11             synchronized (Singleton.class) {
12                 if (instance == null) {
13                     return new Singleton();
14                 }
15             }
16         }
17         return instance;
18     }
19 }

 

为什么要两个if判断?第一个if的主要作用是分流,换句话说,在第一次创建之后,很多的调用可以因为这个if直接被pass掉,根本不进入同步代码块,也就减少了不必要的同步的开销。第二个if是保证这个设计模式逻辑成立的关键。如果没有第二个if,可能发生这种情况:线程A因为instance为null进入了代码块,但是发生了线程调度,线程B也随之进入代码块(这个时候instance还是null)。也许有人会问,这个地方不是用synchronized括起来的吗?怎么线程B还能进入?注意synchronized的部分从来不是不准进入而是进入后会阻塞(如果已经有线程进入了的话)。这样接下来线程B阻塞在这里面,等线程A新建了对象之后,如果没有第二个if就会让线程B也新建一个对象。

 

posted @ 2016-07-25 10:44  DavieTiming  阅读(245)  评论(0编辑  收藏  举报