一、什么是单例模式?
一个类只有一个实例,多个线程同时访问,获取到的也还是同一个对象实例。主要用于解决:一个全局使用的类,被频繁地创建与销毁,从而提高代码的整体性能。
二、如何实现一个单例?
- 构造器私有化
- 暴露一个公共的获取单例对象的接口
三、单例模式的实现
3.1 饿汉式(线程安全)
public class EagerSingleton {
private static EagerSingleton instance = new EagerSingleton();
private EagerSingleton() {}
public static EagerSingleton getInstance() {
return instance;
}
}
3.2 懒汉式(线程不安全)
public class LazySingleton {
private static LazySingleton instance;
private LazySingleton() {}
public static LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
此种方式线程不安全,假设第一次进入,还没有实例对象,当多个线程同时进入到if判断时,可能都会进入到判断中,执行new LazySingleton();
的操作,会导致JVM中存在多个该类的实例对象。
3.3 懒汉式(线程安全)
public class LazySingleton {
private static LazySingleton instance;
private LazySingleton() {}
public static synchronized LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
这种方式虽然是线程安全,但是因为把锁加到方法中后,所有的访问因为需要锁占用,会导致资源浪费。
3.4 双重检查锁(线程安全)
public class DclSingleton {
private static volatile DclSingleton instance;
private DclSingleton() {}
public static DclSingleton getInstance() {
if (instance == null) {
synchronized(DclSingleton.class) {
if (instance == null) {
instance = new DclSingleton();
}
}
}
return instance;
}
}
注意关键字volatile,如果不加可能会出现半初始化的对象。
3.5 静态内部类(线程安全)
public class InnerSingleton {
private static class SingletonHolder {
private static final InnerSingleton INSTANCE = new InnerSingleton();
}
private InnerSingleton(){}
public static InnerSingleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
3.6 枚举(线程安全)
public enum EnumSingleton {
INSTANCE;
public void test() {
System.out.println("Hello World!");
}
}
如果存在继承关系,此种方式不可用。可解决序列化和反序列化、反射入侵等安全问题。
3.7 解决反射入侵
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){
if(singleton != null)
throw new RuntimeException("实例:【"
+ this.getClass().getName() + "】已经存在,该实例只允许实例化一次");
}
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
如果存在实例对象了,还去创建,可以直接抛出一个异常来解决。
3.8 解决序列化和反序列化的安全问题
public class Singleton implements Serializable {
private volatile static Singleton singleton;
private Singleton (){
if(singleton != null)
throw new RuntimeException("实例:【"
+ this.getClass().getName() + "】已经存在,该实例只允许实例化一次");
}
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
public Object readResolve() {
return singleton;
}
}
原因:序列化后,在反序列化的时候,会将反序列化的数据克隆成一个新的实例对象。
解决方案:重写Serializable的readResolve()
方法,将单例的实例对象返回即可。