单例又称单态, 它是和多态相对的.
本质:每一个ClassLoader中指定class创建的实例有且仅有一个.
JDK自带的类有Runtime, 它就是一个典型的单例.
常用的单例的形式有:
Runtime就是饿汉式.
1. 饿汉式
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return INSTANCE;
}
}
2.懒汉式
public class Singleton {
private static Singleton instance = null;
private Singleton() {
}
public static synchronized Singleton getInstance() {
if (instance == null)
instance = new Singleton();
return instance;
}
}
3. DCL
double-checked locking, 它是懒汉式的变体, 主要是为了提高懒汉式的效率. 在jdk1.5之前使用, 会有问题, 由于当时的内存模型(JMM)决定.
因为class代码经过 java 类名 运行时, 为了提速会被JIT转成真正cpu的代码再执行, 这个过程中很可能会对class文件执行的顺序进行优化合并.
具体的在windows中可以将hsdis-i386.dll放到jdk1.7.0/jre/bin/client,和jdk1.7.0/jre/bin/server中, 然后再输入
java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly MyClassName查看
在jdk1.5+时, 对于遇到volatile 时, 其涉及到的方法不会进行优化合并, 所以能保证//2 //3执行的顺序.
public class Singleton {
private static volatile Singleton instance = null;
private Singleton() {
}
public static Singleton instance() {//1
if (instance == null) {//2
synchronized (Singleton.class) {
if (instance == null) {//3
instance = new Singleton();
}
}
}
return instance;
}
}
4.Init On Demand
解决DCL的缺陷, 可以在jdk1.2+使用, 由研究员Bill Pugh提出
public class Singleton {
private Singleton() {
}
private static class SingletonHolder {
public static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
5. 枚举式
最简单的单态, Josh Bloch强烈推荐!
public enum Singleton {
INSTANCE;
}
到这儿为止, 单例讨论完了吗?
提个问题, 单例会不会产生多个对象??
在实际的工作中, 类经常要implements Serializable? 然后再进行对象序列化, 和反序列化
反序列化它可是一个隐含的构造器? 反序列化一下, 单例就失败了.如何解决呢?
常用的方法有两种:
一. readResolve
public class Singleton implements Serializable {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {
}
private Object readResolve() {
//readUnshared 返回该对象的浅表副本
return INSTANCE;
}
public static Singleton getInstance() {
return INSTANCE;
}
}
红色代码非常重要, 当进行反序列化时, ois.readObject();时内部就是通过反射检查implements Serializable的类有没有
readResolve方法,如果有就把readResolve的返回值作为ois.readObject();的返回值. 所以readResolve必须返回之前对象的引用
二. 使用枚举式
使用了枚举特有的特性, 枚举序列化, 反序列化的对象然后是同一个对象. 可以自行查看ois.readObject();oos.writeObject(obj)代码, 它是
把枚举作为一种类型单独考虑的.