ZPY博客

设计模式总结之单例模式(Singleton Pattern)

单例模式可能一般人都认为是所有设计模式中最简单的,但实际上并没有那么简单。不过它的定义是最好理解的。单例模式就是为了确保一个类只有一个实例。

那么,如何保证一个类只有一个实例呢?或者说如何确保一个类不能由其它类new来创建呢?答案是用私有的构造方法和静态方法返回对象。一旦一个类的构造方法声明为private,那么其它类是new不了它的,但是声明了静态方法static,则可以通过类名.静态方法名来得到对象。这样就保证了如果要得到这个类的对象,那么只能通过这个静态方法来拿,而不能自己new。这是不是很酷?

控制了别人不能new之后,接下来的事情就是在静态方法里如何保证类只有一个实例了。我们很容易想到下面的代码。

If ( singleton == null) {

 singleton = new Singleton();

} else {

 return singleton;

}

如果是单线程,这样写是没问题的。但是如何是多线程,那么问题就来了,第一个if可能有多个线程同时进入,这样就会new出多个对象来。最简单的解决方法是在静态方法上加上synchronized关键字,这样确实可以保证线程安全,但是同步会带来性能的下降,而且只有第一次进入方法时才需要同步,当对象创建好后进入方法是不需要同步的。

正确的解决方法是在第一个if里加上同步块synchronized (Singleton.Class){}。这里要注意的是在同步块里还需要做一次null检查,这就是双重检查。为什么这里还需要一次检查?这里有点不好理解。我们仔细想想,假设有2个线程进到了第一个if里,因为加了同步块,这时会一个一个线程的执行同步块里的代码,也就是线程1进入到同步块里,线程2会等待线程1执行完后再进入同步块,当线程1执行完同步块里的代码,new出对象时,线程2会再次执行同步块里代码。这时如果没有第二次检查,会再次new出对象。所以同步块里还需要做null检查。

上面这种方式是延迟实例化对象,也就是说如果一直没有用到这个对象,那么这个对象是不会被实例化的。这种方式也被称为懒汉式。还有一种方法是不管用没用到对象,在类加载时就实例化出来,只需要在定义时加上static关键字直接new出来。也被称为饿汉式。