《Head First设计模式》第五章学习笔记
一、单例模式
二、单例模式实现
实现方式一:不考虑多线程情况
public class Singleton { private static Singleton uniqueInstance; private Singleton() { } public static Singleton getInstance() { if (uniqueInstance == null) { uniqueInstance = new Singleton(); } return uniqueInstance; } }
该实现方式,在多线程的情况下,存在问题:会产生多个实例。
实现方式二:考虑多线程,但牺牲了性能
public class Singleton { private static Singleton uniqueInstance; private Singleton() { } public static synchronized Singleton getInstance() { if (uniqueInstance == null) { uniqueInstance = new Singleton(); } return uniqueInstance; } }
跟实现方式一相比,getInstance方法使用synchronized进行了同步。这解决了多线程并发的问题,但是却因为同步而导致每次访问该方法,都付出了性能的代价(同步一个方法可能造成程序执行效率下降100倍)。
当然,如果getInstance()的性能对应用程序不是很关键(例如访问次数很少),则可以采用这种方式,既简单又有效。如果性能是关键(例如该方法被频繁调用),则需要使用下面的两种方式来实现。
实现方式三:使用“急切”创建实例,而不用延迟实例化的做法(也称为“饿汉式”单例类,与其对应的是“懒汉式”单例类)
public class Singleton { private static Singleton uniqueInstance = new Singleton(); private Singleton() { } public static Singleton getInstance() { return uniqueInstance; } }
利用这个做法,我们依赖JVM在加载这个类时马上创建此唯一的实例。其缺点就是,没有延迟实例化,只要类被加载了,就创建实例,而不管后续是否使用该实例。
实现方式四:用“双重检查加锁”,在getInstance()中减少使用同步
利用双重检查加锁,首先检查实例是否已经创建,如果未创建,则进行同步,然后再次判断是否已创建实例,如果没有,则创建。
public class Singleton { private volatile static Singleton uniqueInstance; private Singleton() { } public static Singleton getInstance() { if (uniqueInstance == null) { synchronized (Singleton.class) { if (uniqueInstance == null) { uniqueInstance = new Singleton(); } } } return uniqueInstance; } }
uniqueInstance,定义成volatile,确保实例化的过程中,多线程能够正确的处理该变量。
注意:双重检查加锁不适用于jdk 1.4及更早的版本。
三、JDK中单例模式的应用例子
JDK中的java.lang.Runtime类,就是使用单例模式,而且使用的是上面介绍的第三种实现方式。如下:
public class Runtime { private static Runtime currentRuntime = new Runtime(); /** * Returns the runtime object associated with the current Java application. * Most of the methods of class <code>Runtime</code> are instance * methods and must be invoked with respect to the current runtime object. * * @return the <code>Runtime</code> object associated with the current * Java application. */ public static Runtime getRuntime() { return currentRuntime; } /** Don't let anyone else instantiate this class */ private Runtime() {} // 其他方法省略 }
四、小结
1、如果有两个及以上的类加载器,加载Singleton类,仍然会导致Singleton类被实例化出多个对象。所以,如果你的程序有多个类加载器,同时你又使用了单例模式,则需要小心这个问题。
2、单例模式是针对同一个JVM而言的,对于不同的JVM,每个JVM都会创建一个对应的实例。