(参考:
深入浅出单实例Singleton设计模式
)什么是单件?
单件就是保证一个类仅有一个实例,并提供一个访问它的全局访问点。——GOF
单件模式简单吗?
简单,的确简单,因为只有一个类。
单件不简单!
其实单件并不见得简单,而且还有点小复杂。其复杂度正是为了保证单件所要达到“仅有一个实例”的宏伟目标而引起的。
当然在一般情况下,单件是简单的。但是在考虑了懒加载、并发、反射、序列化、子类化等诸多因素后,为了保证只有一个实例,复杂度就大大提高了。
下面就从这些方面一一来看如何保证单件只有一个实例,然后看看是不是并不像想象的简单。
1.So easy!饿汉
很简单,直接访问静态域,为防修改,定义成final的。当然构造函数必须是私有的。
/**
* 最简单的单件实现,直接访问静态域
*
* @author nathan
*
*/
public class SimpleSingleton {
public final static SimpleSingleton INSTANCE = new SimpleSingleton();
private SimpleSingleton() {
}
public void doSomething() {
System.out.println("SimpleSingleton.doSomething");
}
}
2.1的变体
通过静态方法访问
/**
* 最简单的单件实现,访问静态方法
*
* @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;
}
}
3.复杂度+1:反反射调用私有构造函数创建实例。
实现方式,在构造函数中判断是否为空,否则抛出异常
/**
* 反反射调用构造函数的单件实现
*
* @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");
}
}
3.复杂度+2:懒汉
为防止加载没用的加载比较耗时的单件
/**
* 懒加载的单件实现,但有并发问题
*
* @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;
}
}
4.复杂度+3:并发控制
懒汉做事总是不靠谱,必须要有额外的机制保证线程安全——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;
}
}
5.懒汉变体
使用静态内部类,也是线程安全的
/**
* 使用静态内部类实现懒汉单例,而且是线程安全的
*
* @author nathan
*
*/
public class HolderSingleton {
private static class SingletonHolder {
private static final HolderSingleton INSTANCE = new HolderSingleton();
}
public static HolderSingleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
6.复杂度+4:接下来考虑序列化
《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;
}
}
7.复杂度+5:跨jvm序列化
第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();
}
}
8.复杂度+6:考虑单件的继承
在GOF的《设计模式》中给出了方案,即采用register的方式。但按其书中描述,子类的构造函数必须是公有的,这就违背了单例的初衷。因此必须借助“反射”机制实现对子类的实例化。
9.复杂度+7:再考虑基于继承的单件体系的跨jvm的序列化
(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);
}
}