单例模式
基础概念
单例模式是什么有什么用
单例模式保证一个类只有一个实例,并提供一个可以访问该实例的方法
单例模式的几种写法
-
饿汉式
饿汉式是在初始化时就将单例对象创建出来。通常通过属性new创建自身。由JVM保证线程安全,但会造成内存资源的浪费
//饿汉式 public class Singleton { //私有成员变量 private static Singleton singleton; private Singleton(){ } public static Singleton getInstance(){ return singleton; } }
-
懒汉式
懒汉式是指在第一次使用的时候才将对象创建出来,是线程不安全的,但是不会造成内存资源的浪费
//懒汉式 public class Singleton { private static Singleton singleton = null; private Singleton(){ } public static Singleton getInstance(){ if(singleton == null) { singleton = new Singleton(); } return singleton; } }
这种方法的不安全性主要在当还没有对象时,有多个线程同时运行,发现没有实例对象,就自己生成了实例对象,这样就有可能出现多个实例对象
-
双重校验锁
//双重校验锁 public class Singleton { //使用volatile关键字保证指令不会被重排序 private volatile static Singleton singleton = null; private Singleton(){ } public static Singleton getInstance(){ //如果没有进行实例化 if(singleton == null) { //将整个类锁住 synchronized (Singleton.class) { //确保没有进行实例化 if(singleton == null) { singleton = new Singleton(); } } } return singleton; } }
-
静态内部类
//静态内部类 public class StaticSingleton { //私有化构造器 private StaticSingleton() { } //使用内部类创建对象,由JVM保证线程安全 private static class SingletonFactory { private static StaticSingleton singleton = new StaticSingleton(); } public static StaticSingleton getInstance() { return SingletonFactory.singleton; } }
-
枚举
public enum SingletonEnum { INSTANCE; }
如何破坏单例模式
-
反射
通过反射获取单例对象的构造器,暴力破解后即可创建多个实例
@Test public static void reflectTest() { Singleton singleton = Singleton.getInstance(); Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor(); constructor.setAccessible(true); Singleton singleton2 = constructor.newInstance(null); System.out.println(singleton == singleton2); }
-
序列化
通过深克隆复制对象,可以生成多个实例
@Test public static void serializationTest() { ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("objectFile")); Singleton singleton3 = Singleton.getInstance(); os.writeObject(singleton3); os.close(); ObjectInputStream is = new ObjectInputStream(new FileInputStream(new File("objectFile"))); Singleton singleton4 = (Singleton) is.readObject(); is.close(); System.out.println(singleton3 == singleton4); }
如何防止单例模式被破坏
-
防止反射破坏单例模式
新增一个volatile变量
isFirstCreate
,在私有构造方法里面对该变量进行双校验。 -
防止序列化破坏单例模式
继承Serializable接口,并重写readObject()方法
import java.io.ObjectStreamException;
import java.io.Serializable;
// 能防止被反射、序列化破坏的双重校验单例模式
public class Singleton implements Serializable {
private volatile static Singleton singleton = null;
private volatile static boolean isFirstCreate = true;
private Singleton(){
//防止被反射破坏单例
if (isFirstCreate) {
synchronized (Singleton.class) {
if(isFirstCreate) {
isFirstCreate = false;
}
else {
throw new RuntimeException("此单例已经创建过了");
}
}
}
else {
throw new RuntimeException("此单例已经存在");
}
}
public static Singleton getInstance(){
if(singleton == null) {
synchronized (Singleton.class) {
if(singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
//防止被序列化生成多个实例
private Object readResolve() throws ObjectStreamException {
return singleton;
}
}