前置文章: http://blog.csdn.net/xiayiguo/article/details/79369480 (单例模式入门与使用)
单例模式实现的不同方式
两个方式的名称比较形象:懒汉式和饿汉式。可以参看下面的代码。懒汉式在于懒,所以直到最后才会生成需要单例对象;而饿汉式由于饿,在类装载的时候就创建了单例对象。
懒汉式
public class LazySingleton
{
// step 2
private static LazySingleton lazySingleton ;
// step 1
private LazySingleton() {
}
// step 3
public static LazySingleton getInstance () {
if (lazySingleton == null){
lazySingleton = new LazySingleton();
}
return lazySingleton;
}
}
饿汉式
public class HungrySingleton
{
// step 2
private static HungrySingleton hungrySingleton = new HungrySingleton();
// step 1
private HungrySingleton() {
}
// step 3
public static HungrySingleton getInstance () {
return hungrySingleton;
}
}
单例的懒汉式实现体现了编程中延迟加载(懒加载)的思想,同时也体现了缓存的思想。
单例模式应用
缓存实现
public class MyJavaCache
{
private Map<String, Object> cache = new HashMap<String, Object>();
public Object getValue(String key) {
Object object = cache.get(key);
if (object == null) {
object = key + "value";
cache.put(key, object);
}
return object;
}
}
单例模式优缺点
懒汉式是典型的时间换空间,每次获取实例时,都需要进行判断,从而浪费一定的时间,反过来,如果一直没人使用,则不需要节约内存空间。
饿汉式是典型的空间换时间,在类转载时,就创建了类实例,不管你用不用,先创建出来,节约了判断时间。
模式优化
饿汉式是线程安全的,因为虚拟机保证只会装载一次,在类装载的时候是不会发生并发的。
那如何实懒汉式的线程安全呢?可以直接在 getInstance方法上加synchrized 关键字。但这样会降低系统访问速度,而且每次都需要判断。有没有更好的方式呢?
双重检查加锁
双重检查加锁的意思是,并需要每次进如 getInstance方法都进行加锁,而是先不同步,进入方法后,先判断对象是否存在,如果不存在,才进入同步块,再次检查对象是否存在,如果不存在,创建对象。这样只会在第一次对象存在的情况,进入同步块。 代码示例如下:
public class Singleton {
/**
* 对保存实例的变量添加volatile的修饰
*/
private volatile static Singleton instance = null;
private Singleton(){
}
public static Singleton getInstance(){
//先检查实例是否存在,如果不存在才进入下面的同步块
if(instance == null){
//同步块,线程安全的创建实例
synchronized(Singleton.class){
//再次检查实例是否存在,如果不存在才真的创建实例
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
本文完整代码,可以参见:https://github.com/TrumanXia/design_patterns