懒汉模式
//使用的时候才实例化
public class LazySingleton {
//私有化构造器
private LazySingleton() {
if(HungrySingleton.instance != null)
throw new RuntimeException("单例模式不可多个对象");
}
/**
* volatile:防止重排序,一般new一个instance会执行以下三步
* 1. 分配空间(堆空间)->返回一个指向该空间的一个内存引用
* 2. 对该空间进行初始化
* 3. 把内存引用赋值给instance变量
* 若此时编译器重排序,则可能变成1-3-2的顺序
* 此时若另一个线程进来,会得到一个instance变量不为空,但其指向的地址为空,报错
*/
private static volatile LazySingleton instance;
public static LazySingleton getInstance(){
if(instance == null){
synchronized (LazySingleton.class){
/**
* 双重锁
* 1. 如果synchronized static方法,则会锁住整个类,代价太大
* 2. 可在判断为null后再加锁
* 3. 因为可能同时很多线程判断为null,所以等待锁
* 4. 此时要再加一次判定,为了只new一个对象
*/
if(instance == null){
instance = new LazySingleton();
}
}
}
return instance;
}
}
- 保证线程安全
- 防止指令重排
- 通过双重检查优化加锁过程
饿汉模式
// 在类加载阶段就完成了实例的初始化。通过类加载机制,来保证线程安全
public class HungrySingleton {
private static HungrySingleton instance = new HungrySingleton();
public static HungrySingleton getInstance(){
return instance;
}
private HungrySingleton(){
if(HungrySingleton.instance != null)
throw new RuntimeException("单例模式不可多个对象");
};
}
静态内部类
public class InnerClassSingleton {
static class InnerClass{
private static InnerClassSingleton instance = new InnerClassSingleton();
}
/**
* 1. 调用该方法触发InnerClass的加载
* 2. 利用类加载机制,对instance进行初始化,同时保证线程安全
*/
public static InnerClassSingleton getInstance(){
return InnerClass.instance;
}
private InnerClassSingleton(){
//防止利用反射获取构造方法创建对象
if(InnerClass.instance != null);
throw new RuntimeException("单例模式不可多个对象");
};
}
序列化
- 继承Serializable的实体类
public class SerializableEntity implements Serializable {
static final long serialVersionUID = 42L; //写死,程序改变类才可以跟磁盘的兼容
private static SerializableEntity instance = new SerializableEntity();
@Override
protected Object clone() throws CloneNotSupportedException {
throw new RuntimeException("单例模式禁止克隆");
}
public static SerializableEntity getInstance(){
return instance;
}
private SerializableEntity(){
if(instance!=null)
throw new RuntimeException("单例模式只有一个对象");
};
}
- 反序列化获取对象测试
//先将对象序列化并写入磁盘
SerializableEntity instance = SerializableEntity.getInstance();
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("instance"));
outputStream.writeObject(instance);
outputStream.close();
//从磁盘反序列化得到对象
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("instance"));
Object o = inputStream.readObject();
inputStream.close();
SerializableEntity entity = (SerializableEntity) o;
System.out.println(instance == entity); //false:说明是两个
- 解决
//在实体类加入该方法
Object readResolve() throws ObjectStreamException {
return instance;
}
枚举类
枚举类型默认单例模式,即里面的属性都是同一个对象