单件就是保证一个类仅有一个实例,并提供一个访问它的全局访问点。——GOF
简单,的确简单,因为只有一个类。
其实单件并不见得简单,而且还有点小复杂。其复杂度正是为了保证单件所要达到“仅有一个实例”的宏伟目标而引起的。
当然在一般情况下,单件是简单的。但是在考虑了懒加载、并发、反射、序列化、子类化等诸多因素后,为了保证只有一个实例,复杂度就大大提高了。
下面就从这些方面一一来看如何保证单件只有一个实例,然后看看是不是并不像想象的简单。
很简单,直接访问静态域,为防修改,定义成final的。当然构造函数必须是私有的。
/** * 最简单的单件实现,直接访问静态域 * * @author nathan * */ public class SimpleSingleton { public final static SimpleSingleton INSTANCE = new SimpleSingleton(); private SimpleSingleton() { } public void doSomething() { System.out.println("SimpleSingleton.doSomething"); } }
通过静态方法访问
/** * 最简单的单件实现,访问静态方法 * * @author nathan * */ public class SimpleSingleton2 { private final static SimpleSingleton2 INSTANCE = new SimpleSingleton2(); private SimpleSingleton2() { } public void doSomething() { System.out.println("SimpleSingleton2.doSomething"); } public static SimpleSingleton2 getInstance() { return INSTANCE; } }
实现方式,在构造函数中判断是否为空,否则抛出异常
/** * 反反射调用构造函数的单件实现 * * @author nathan * */ public class AntiRefSingleton { public static AntiRefSingleton instance = new AntiRefSingleton(); private AntiRefSingleton() { if (instance != null) { throw new RuntimeException( "This is a Singleton Class, please use AntiRefSingleton.instance to get the Object!"); } } public void doSomething() { System.out.println("SimpleSingleton.doSomething"); } }
为防止加载没用的加载比较耗时的单件
/** * 懒加载的单件实现,但有并发问题 * * @author nathan * */ public class LazySingleton { private static LazySingleton instance = null; private LazySingleton() { if (instance != null) { throw new RuntimeException( "This is a Singleton Class, please use the getInstance function to get the Object!"); } } public void doSomething() { System.out.println("LazySingleton.doSomething"); } public static LazySingleton getInstance() { if (instance == null) { instance = new LazySingleton(); } return instance; } }
懒汉做事总是不靠谱,必须要有额外的机制保证线程安全——DCL(双重检查加锁)
/** * 使用DCL技术实现的并发安全的懒加载单件实现 * * @author nathan * */ public class ConcurrentSingleton { /** * 必须声明为volatile的 */ private static volatile ConcurrentSingleton instance = null; private ConcurrentSingleton() { if (instance != null) { throw new RuntimeException( "This is a Singleton Class, please use the getInstance function to get the Object!"); } } public void doSomething() { System.out.println("ConcurrentSingleton.doSomething"); } public static ConcurrentSingleton getInstance() { if (instance == null) {// 使用双重检查锁定技术 synchronized (ConcurrentSingleton.class) { if (instance == null) { instance = new ConcurrentSingleton(); } } } return instance; } }
使用静态内部类,也是线程安全的
/** * 使用静态内部类实现懒汉单例,而且是线程安全的 * * @author nathan * */ public class HolderSingleton { private static class SingletonHolder { private static final HolderSingleton INSTANCE = new HolderSingleton(); } public static HolderSingleton getInstance() { return SingletonHolder.INSTANCE; } }
《Effective Java》中作者已经给出了方案,即添加readResolve方法。如下:
/** * 可序列化的单件实现(同时是并发安全的懒加载的),但只能在同一个jvm中使用,不能跨jvm * * @author nathan * */ public class SerializableSingleton implements Serializable { private static final long serialVersionUID = 5691590550973506283L; private transient String description; public void doSomething() { description = "SerializableSingleton"; } @Override public String toString() { return super.toString() + " [description=" + description + "]"; } /** * 必须声明为volatile的 */ private static volatile transient SerializableSingleton instance = null; private SerializableSingleton() { if (instance != null) { throw new RuntimeException( "This is a Singleton Class, please use the getInstance function to get the Object!"); } } public static SerializableSingleton getInstance() { if (instance == null) {// 使用双重检查锁定技术 synchronized (SerializableSingleton.class) { if (instance == null) { instance = new SerializableSingleton(); } } } return instance; } private Object readResolve() { // 抛弃反序列化的实例,返回原实例 return instance; } }
第6中方案作者给出了解决问题的思路,但未真正解决序列化问题。因为它只能在同一个jvm中适应。但是在同一个jvm中序列化单例似乎意义不大。下面是kuajvm的单例实现方式。简单修改6中的readResolve方法即可。
/** * 能跨jvm使用的可序列化的单件实现(同时是并发安全、懒加载的) * * @author nathan * */ public class SerializableSingleton2 implements Serializable { private static final long serialVersionUID = 5691590550973506283L; private String description; private int count; public void doSomething() { description = "SerializableSingleton2"; count = 100; } @Override public String toString() { return super.toString() + " [description=" + description + ",count=" + count + "]"; } public void setCount(int count) { this.count = count; } public int getCount() { return count; } /** * 必须声明为volatile的 */ private static volatile SerializableSingleton2 instance = null; private SerializableSingleton2() { if (instance != null) { throw new RuntimeException( "This is a Singleton Class, please use the getInstance function to get the Object!"); } } public static SerializableSingleton2 getInstance() { if (instance == null) {// 使用双重检查锁定技术 synchronized (SerializableSingleton2.class) { if (instance == null) { instance = new SerializableSingleton2(); } } } return instance; } private Object readResolve() { if (instance == null) {// 使用双重检查锁定技术 synchronized (SerializableSingleton2.class) { if (instance == null) { instance = this;// 如果是第一次反序列化,则使用该实例,否则不管它 } } } return instance; } public static void serialize(String file) throws Exception { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream( new File(file))); oos.writeObject(instance); } public static void antiSerialize(String file) throws Exception { ObjectInputStream ois = new ObjectInputStream(new FileInputStream( new File(file))); instance = (SerializableSingleton2) ois.readObject(); } }
在GOF的《设计模式》中给出了方案,即采用register的方式。但按其书中描述,子类的构造函数必须是公有的,这就违背了单例的初衷。因此必须借助“反射”机制实现对子类的实例化。
(8和9合后的并代码如下)
/** * 一个可子类化、可序列化的单件实现 * * @author nathan * */ public class SubableSingleton implements Serializable { private String description; private int count; public void doSomething() { description = "SubableSingleton"; count = 100; } @Override public String toString() { return super.toString() + " [description=" + description + ",count=" + count + "]"; } public void setCount(int count) { this.count = count; } public int getCount() { return count; } // 以下代码实现单件支持 private static final long serialVersionUID = 5713856529741473199L; private static SingletonHolder holder = null; private String name; protected SubableSingleton() { this(SubableSingleton.class); } protected SubableSingleton(Class<? extends SubableSingleton> clazz) { if (holder.lookup(clazz.getName()) != null) { throw new RuntimeException( "This is a Singleton Class, please use getInstance function to get the Object!"); } name = clazz.getName(); } /** * 注意:使用synchronized代替DCL进行简单并发控制 * * @param clazz * @return */ public static synchronized SubableSingleton getInstance( Class<? extends SubableSingleton> clazz) { if (holder == null) { holder = new SingletonHolder(); } SubableSingleton instance = holder.lookup(clazz.getName()); if (instance == null) { try { Constructor<? extends SubableSingleton> constructor = clazz .getDeclaredConstructor(); constructor.setAccessible(true); instance = constructor.newInstance(); holder.register(clazz.getName(), instance); } catch (Exception e) { e.printStackTrace(); } } return instance; } private synchronized Object readResolve() { System.out.println("SubableSingleton.readResolve"); if (holder == null) { holder = new SingletonHolder(); holder.register(this.name, this); } return holder.lookup(this.name); } public static void serialize(String file) throws Exception { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream( new File(file))); oos.writeObject(holder); } public static void antiSerialize(String file) throws Exception { ObjectInputStream ois = new ObjectInputStream(new FileInputStream( new File(file))); holder = (SingletonHolder) ois.readObject(); } /** * 单例持有类,私有 * * @author nathan * */ private static class SingletonHolder implements Serializable { private static final long serialVersionUID = -4221190210772287103L; private Map<String, SubableSingleton> registry = new HashMap<String, SubableSingleton>(); public void register(String name, SubableSingleton subableSingleton) { registry.put(name, subableSingleton); } public SubableSingleton lookup(String name) { return registry.get(name); } private synchronized Object readResolve() { System.out.println("SingletonHolder.readResolve"); // 抛弃反序列化的实例,返回原实例 if (holder == null) { holder = this; } return holder; } } }
/** * 子类必须在构造函数中调用父类的带参构造函数,完成反反射控制 * @author nathan * */
public class SubSingleton extends SubableSingleton { private static final long serialVersionUID = 2430773476223417288L; protected SubSingleton() { super(SubSingleton.class); } }
那么,你还认为单件简单吗?欢迎交流!
GOF的《设计模式》
《Effective Java》
《单件模式的7种写法》http://www.360doc.com/content/10/1213/09/2703996_77599342.shtml
public class SingletonTest { @Test public void testSimpleSingleton() { Assert.assertEquals(SimpleSingleton.INSTANCE, SimpleSingleton.INSTANCE); } @Test public void testSimpleSingleton2() { Assert.assertEquals(SimpleSingleton2.getInstance(), SimpleSingleton2.getInstance()); } @Test public void testAntiRefSingleton() throws Exception { Assert.assertEquals(AntiRefSingleton.instance, AntiRefSingleton.instance); try { Constructor<AntiRefSingleton> constructor = AntiRefSingleton.class .getDeclaredConstructor(); constructor.setAccessible(true); constructor.newInstance(); } catch (Exception e) { e.printStackTrace(); } } @Test public void testLazySingleton() { Assert.assertEquals(LazySingleton.getInstance(), LazySingleton.getInstance()); } @Test public void testConcurrentSingleton() { Assert.assertEquals(ConcurrentSingleton.getInstance(), ConcurrentSingleton.getInstance()); } @Test public void testSubableSingleton() { Assert.assertNotNull(SubableSingleton .getInstance(SubableSingleton.class)); Assert.assertTrue(SubableSingleton.getInstance(SubableSingleton.class) instanceof SubableSingleton); Assert.assertEquals( SubableSingleton.getInstance(SubableSingleton.class), SubableSingleton.getInstance(SubableSingleton.class)); Assert.assertNotNull(SubableSingleton.getInstance(SubSingleton.class)); Assert.assertTrue(SubableSingleton.getInstance(SubSingleton.class) instanceof SubSingleton); Assert.assertEquals(SubableSingleton.getInstance(SubSingleton.class), SubableSingleton.getInstance(SubSingleton.class)); } @Test public void testSubableSingletonSerialize() throws Exception { SubableSingleton instance = SubableSingleton .getInstance(SubableSingleton.class); SubableSingleton instance2 = SubableSingleton .getInstance(SubSingleton.class); instance.doSomething(); instance2.doSomething(); SubableSingleton.serialize("testSubableSingletonSerialize.jser"); SubableSingleton.antiSerialize("testSubableSingletonSerialize.jser"); Assert.assertEquals(instance, SubableSingleton.getInstance(SubableSingleton.class)); Assert.assertEquals(instance2, SubableSingleton.getInstance(SubSingleton.class)); } @Test public void testSubableSingletonSerialize2() throws Exception { SubableSingleton.antiSerialize("testSubableSingletonSerialize.jser"); SubableSingleton instance = SubableSingleton .getInstance(SubableSingleton.class); SubableSingleton instance2 = SubableSingleton .getInstance(SubSingleton.class); Assert.assertEquals(100, instance.getCount()); Assert.assertEquals(100, instance2.getCount()); instance.setCount(20); SubableSingleton.antiSerialize("testSubableSingletonSerialize.jser"); instance = SubableSingleton.getInstance(SubableSingleton.class); Assert.assertEquals(20, instance.getCount()); } @Test public void testSubableSingletonSerialize3() throws Exception { SubableSingleton instance = SubableSingleton .getInstance(SubableSingleton.class); instance.doSomething(); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream( new File("testSubableSingletonSerialize.jser"))); oos.writeObject(instance); ObjectInputStream ois = new ObjectInputStream(new FileInputStream( new File("testSubableSingletonSerialize.jser"))); SubableSingleton instance2 = (SubableSingleton) ois.readObject(); Assert.assertEquals(instance, instance2); } @Test public void testSubableSingletonSerialize4() throws Exception { ObjectInputStream ois = new ObjectInputStream(new FileInputStream( new File("testSubableSingletonSerialize.jser"))); SubableSingleton instance2 = (SubableSingleton) ois.readObject(); Assert.assertEquals(100, instance2.getCount()); instance2.setCount(20); ois = new ObjectInputStream(new FileInputStream(new File( "testSubableSingletonSerialize.jser"))); instance2 = (SubableSingleton) ois.readObject(); Assert.assertEquals(20, instance2.getCount()); } @Test public void testSubableSingletonSerialize5() throws Exception { SubableSingleton instance = SubableSingleton .getInstance(SubSingleton.class); SubableSingleton instance2 = SubableSingleton .getInstance(SubSingleton.class); instance.doSomething(); instance2.doSomething(); SubableSingleton.serialize("testSubableSingletonSerialize5.jser"); SubableSingleton.antiSerialize("testSubableSingletonSerialize5.jser"); Assert.assertEquals(instance, SubableSingleton.getInstance(SubSingleton.class)); Assert.assertEquals(instance2, SubableSingleton.getInstance(SubSingleton.class)); Assert.assertEquals(100, instance2.getCount()); } @Test public void testSerializableSingletonInOneJvm() throws IOException, ClassNotFoundException { Assert.assertEquals(SerializableSingleton.getInstance(), SerializableSingleton.getInstance()); SerializableSingleton singleton = SerializableSingleton.getInstance(); singleton.doSomething(); System.out.println(singleton); ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(singleton); ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bais); SerializableSingleton clone = (SerializableSingleton) ois.readObject(); System.out.println(clone); Assert.assertEquals(singleton, clone); clone.doSomething(); } /** * 在同一个jvm中,序列化后再反序列化对单件来说是无效的,jvm中始终使用的是最初创建的那个单件实例 * * @throws Exception */ @Test public void testSerializableSingleton2InOneJvm() throws Exception { Assert.assertEquals(SerializableSingleton2.getInstance(), SerializableSingleton2.getInstance()); SerializableSingleton2 singleton = SerializableSingleton2.getInstance(); singleton.doSomething(); Assert.assertEquals(SerializableSingleton2.getInstance().getCount(), 100); SerializableSingleton2.serialize("SerializableSingleton2.jser"); // 序列化后改变实例数据 SerializableSingleton2.getInstance().setCount(30); // 反序列化,实际上并未使用反序列化出来的实例,而是继续使用原来的实例,因为在同一个Jvm中 SerializableSingleton2.antiSerialize("SerializableSingleton2.jser"); // 因此这里的值是30,不是序列化时候的100 Assert.assertEquals(SerializableSingleton2.getInstance().getCount(), 30); // 改变实例数据 SerializableSingleton2.getInstance().setCount(20); // 再反序列化 SerializableSingleton2.antiSerialize("SerializableSingleton2.jser"); Assert.assertEquals(SerializableSingleton2.getInstance().getCount(), 20); } /** * 在另一个jvm中启动反序列化<br> * 注意:请先执行测试testSerializableSingleton2InOneJvm,再执行该测试 * * @throws Exception */ @Test public void testSerializableSingleton2NotInOneJvmRead() throws Exception { // 反序列化,并创建单件实例,此后在该jvm中将一直使用该实例 SerializableSingleton2.antiSerialize("SerializableSingleton2.jser"); // 因此这里的值是序列化时候的100 Assert.assertEquals(SerializableSingleton2.getInstance().getCount(), 100); // 改变实例数据 SerializableSingleton2.getInstance().setCount(20); // 再反序列化 SerializableSingleton2.antiSerialize("SerializableSingleton2.jser"); Assert.assertEquals(SerializableSingleton2.getInstance().getCount(), 20); } }