写单例模式,总是让我想起孔乙己写“茴香豆”的茴的四种写法,虽然感觉他比较迂腐,但是在说到单例模式时,却也是忍不住的要说单例模式也有好几种写法,这样算来,自己也算迂腐了。不过不管怎样,既然是做技术的,多掌握一点也是比较好的,同时也将积累的分享给大家,大家各凭所好吧。
在说“茴”字之前,需要先说下什么是“茴香豆”,也就是我们的单例模式。单例模式就是让一个对象只产生一个实例,并且对外提供一个全局的方法对其进行调用,通常来说方法名都使使用getInstance()。
再来说说,单例模式的整体架构。
(1)私有的构造函数
(2)私有的静态实例,该实例禁止外部访问。
(3)一个公有的静态工厂方法getInstance(),有时是需要同步的。
(4)有时也可以重写clone()方法。
下面,再来说说,这个“茴”字有几种写法。
第一种,饿汉式,所谓饿汉式就是主动去new一个对象,然后返回。见代码:
/** * 单例模式之饿汉式 * @author xiAoT * */ public class Singleton001 { private static Singleton001 instance = new Singleton001(); private Singleton001(){} public static Singleton001 getInstance(){ return instance; } @Override protected Singleton001 clone() throws CloneNotSupportedException { return instance; } }
饿汉式的缺点是不论使不使用该类,都会在内存中实例化一个对象;优点是因为采用static类型,static本身实现了同步,所以解决了同步的问题,性能比较高,一般情况下推荐使用。
第二种,懒汉式,相当于延迟加载,在需要的时候再创建资源。
/** * 单例模式之懒汉式 * @author xiAoT * */ public class Singleton002 { private static Singleton002 instance = null; private Singleton002(){} public synchronized static Singleton002 getInstance(){ if(instance == null){ instance = new Singleton002(); } return instance; } }
由于采用了同步,在性能上会打折扣。
第三种,即采用缓存的方式,common.logutil中创建Log类就是采用这个方式,见LogFactory.java。基本实现方式就是用一个Map存放对象,需要使用的时候就从map中取,为了实现同步,我们一般用Hashtable存储。
/** * 单例模式之采用缓存 * @author xiAoT * */ public class Singleton003 { private final static String CACHE_KEY = "instance";//定义一个key常量 private Singleton003(){} private static Hashtable<String,Singleton003> cacheMap = new Hashtable<String, Singleton003>(); public synchronized static Singleton003 getInstance(){ Singleton003 instance = cacheMap.get(CACHE_KEY); if(instance == null){ cacheMap.put(CACHE_KEY, instance = new Singleton003()); } return instance; } }
第四种,这个方式就神了,即可以实现延迟加载,又能够节省资源,该方式主要采用类级内部类和多线程同步缺省机制实现。
如下几种情况,JDK内部实现了同步:
1. 有static{}修饰的块或者static修改的变量初始化数据;
2. 访问final字段时
3. 在创建线程之前创建对象时
4. 线程可以看见它将要处理的对象时
/** * 单例模式之采用类级内部类和多线程同步缺省机制 * @author xiAoT * */ public class Singleton004 { /** * 类级内部类 * @author xiAoT * */ private static class SingletonHolder{ private static Singleton004 instance = new Singleton004(); } private Singleton004(){} public static Singleton004 getInstance(){ return SingletonHolder.instance; } }
类级内部类相当于外部类的成员,只有在第一次使用时才会被加载。
关于反序列化
如果采用了单例的类实现了序列化接口后,在进行反序列时,会自动创建新的类,为了解决这个问题,可以采用readResolve()方法
/** * 反序列化 * * @author xiAoT * */ public class Singleton005 implements Serializable { private Singleton005() { } private static final Singleton005 INSTANCE = new Singleton005(); public static Singleton005 getInstance() { return INSTANCE; } private Object readResolve() throws ObjectStreamException { return INSTANCE; } }
======================================待续===============================