单例模式写法

时间:2021-08-17 05:33:54

单例模式

一个类有且仅有一个实例,并且自行实例化向整个系统提供

基本用法

五种用法:懒汉,饿汉,双重校验锁,枚举和静态内部类。

饿汉式

加载时实例化

public class SingletonA {
private static SingletonA instance = new SingletonA();

private SingletonA() {
}

public static SingletonA getInstance() {
return instance;
}
}

懒汉式

使用时实例化

public class SingletonB {
private static SingletonB instance;

private SingletonB() {
}

public static synchronized SingletonB getInstance() {
if (instance == null) {
instance = new SingletonB();
}
return instance;
}
}

双重校验锁

volatile修饰的变量的值,将不会被本地线程缓存,所有对该变量的读写都是直接操作共享内存,从而确保多个线程能正确的处理该变量。

由于volatile关键字可能会屏蔽掉虚拟机中的一些必要的代码优化,所以运行效率并不是很高。

public class SingletonC {
private volatile static SingletonC instance;

private SingletonC() {
}

public static SingletonC getInstance() {
// 先检查实例是否存在,如果不存在才进入下面的同步块
if (instance == null) {
// 同步块,线程安全的创建实例
synchronized (SingletonC.class) {
// 再次检查实例是否存在,如果不存在才真正的创建实例
if (instance == null) {
instance = new SingletonC();
}
}
}
return instance;
}
}

静态内部类

public class SingletonD {
private static class InstanceHolder {
public static final SingletonD INSTANCE = new SingletonD();
}

private SingletonD() {
}

public static final SingletonD getInstance() {
return InstanceHolder.INSTANCE;
}
}

枚举

不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。1.5中才加入enum特性

public enum SingletonE {
INSTANCE;

public void otherMethod() {
Log.d("Singleton", "E");
}
}

特别用法

集合管理

public class SingletonManager {
private static Map<String, Object> objMap = new HashMap<String, Object>();

private SingletonManager() {
}

public static void putInstance(String key, Object instance) {
if (!objMap.containsKey(key)) {
objMap.put(key, instance);
}
}

public static Object getInstance(String key) {
if (objMap.containsKey(key)) {
return objMap.get(key);
}
return null;
}
}

问题

类加载器

如果单例由不同的类加载器载入,那便有可能存在多个单例类的实例。假定不是远端存取,例如一些servlet容器对每个servlet使用完全不同的类加载器,这样的话如果有两个servlet访问一个单例类,它们就都会有各自的实例。

解决方法:

private static Class getClass(String classname) throws ClassNotFoundException {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if(classLoader == null){
classLoader = Singleton.class.getClassLoader();
}
return classLoader.loadClass(classname);
}

序列化

如果Singleton实现了java.io.Serializable接口,那么这个类的实例就可能被序列化和反序列化。不管怎样,如果你序列化一个单例类的对象,接下来反序列化多个那个对象,那你就会有多个单例类的实例。

解决方法:

public class SingletonF implements java.io.Serializable{

private static SingletonF instance = new SingletonF();

private SingletonF() {
}

public static SingletonF getInstance() {
return instance;
}

// 添加此方法确保序列化单例
private Object readResolve() throws java.io.ObjectStreamException {
return instance;
}
}