常见单例模式
1.饿汉式
/**
* 缺点:有现场创建资源,如果没用到造成内存浪费
*/
public class HungrySingleton implements Serializable {
/**
* 私有化构造方法
*/
private HungrySingleton() {
}
/**
* 或者 静态代码块初始化
* private static final HungrySingleton HUNGRY_SINGLETON;
* static{
* HUNGRY_SINGLETON = new HungrySingleton();
* }
*/
private static final HungrySingleton HUNGRY_SINGLETON = new HungrySingleton();
public static HungrySingleton getInstance() {
return HUNGRY_SINGLETON;
}
}
测试用例
public class HungrySingletonTest {
/**
* 单例校验
*/
@Test
public void testSingleton() {
Set obj = new ConcurrentSkipListSet();
CountDownLatch countDownLatch = new CountDownLatch(100);
for (int i = 0; i < 100; i++) {
new Thread(() -> {
HungrySingleton instance = HungrySingleton.getInstance();
if (obj.add(instance.toString())) {
System.out.println(instance.toString());
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
countDownLatch.countDown();
}
try {
System.in.read();
} catch (IOException e) {
e.printStackTrace();
}
}
}
2.懒汉式
public class LazySingleton implements Serializable {
private LazySingleton() {
}
private static LazySingleton LAZY_SINGLETON;
/**
* synchronized 关键字添加锁,性能下降
* @return
*/
public static synchronized LazySingleton getInstance() {
if (LAZY_SINGLETON == null) {
LAZY_SINGLETON = new LazySingleton();
}
return LAZY_SINGLETON;
}
}
3. Double Check 懒汉式
public class DoubleCheckSingleton {
private DoubleCheckSingleton() {
}
/**
* volatile 关键字防止 JVM指令重排序
*/
private volatile static DoubleCheckSingleton DOUBLECHECK_SINGLETON;
public static DoubleCheckSingleton getInstance() {
/**
* synchronized不加在方法上 确保只在初始化时同步
* 但是如果不用volatile修饰,存在指令重排序问题
* DOUBLECHECK_SINGLETON = new DoubleCheckSingleton();可分解为指令
* memory = allocate(); //1:分配对象的内存空间
* ctorInstance(memory); //2:初始化对象
* instance = memory; //3:设置instance指向刚分配的内存地址
* JVM指令重排序后可能出现
* memory = allocate(); //1:分配对象的内存空间
* instance = memory; //3:设置instance指向刚分配的内存地址,此时对象还没有被初始化!
* ctorInstance(memory); //2:初始化对象
*
*/
if (DOUBLECHECK_SINGLETON == null) {
synchronized (DoubleCheckSingleton.class) {
if (DOUBLECHECK_SINGLETON == null) {
DOUBLECHECK_SINGLETON = new DoubleCheckSingleton();
}
}
}
return DOUBLECHECK_SINGLETON;
}
}
4.静态内部类
public class InnerClassSingleton {
private InnerClassSingleton() {
}
/**
* 调用InnercClassHolder才创建实例
*
* @return
*/
public static InnerClassSingleton getInstance() {
return InnerClassHolder.LAZY;
}
private static class InnerClassHolder {
private static final InnerClassSingleton LAZY = new InnerClassSingleton();
}
}
5.枚举实现注册式
Effective Java 推荐实现方式,枚举天生不支持反射创建,也不存在序列化破坏单例问题
public enum RegSingleton {
INSTANCE;
public int doSomeing() {
return INSTANCE.hashCode();
}
}
测试用例
@Test
public void testSingleton() {
Set obj = new ConcurrentSkipListSet();
CountDownLatch countDownLatch = new CountDownLatch(100);
for (int i = 0; i < 100; i++) {
new Thread(() -> {
if (obj.add(RegSingleton.INSTANCE.doSomeing())) {
System.out.println(RegSingleton.INSTANCE.doSomeing());
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
countDownLatch.countDown();
}
try {
System.in.read();
} catch (IOException e) {
e.printStackTrace();
}
}
反射对单例的破坏
/**
* 反射破坏单例
* 解决方案 在私有构造方法抛出异常
*/
@Test
public void refSingleton() {
try {
Class<?> clazz = HungrySingleton.class;
Constructor<?> declaredConstructor = clazz.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
HungrySingleton instance1 = (HungrySingleton)
declaredConstructor.newInstance();
System.out.println(instance1);
HungrySingleton instance2 = HungrySingleton.getInstance();
System.out.println(instance2);
System.out.println(instance1 == instance2);
} catch (Exception e) {
e.printStackTrace();
}
}
解决方案
/**
* 解决反射破坏单例
*/
private HungrySingleton() {
if (HUNGRY_SINGLETON != null) {
throw new RuntimeException("不允许反射创建实例");
}
}
序列化对单例的破坏
/**
* 序列化破坏单例
*/
@Test
public void seriable() {
try {
HungrySingleton instance = HungrySingleton.getInstance();
FileOutputStream out = new FileOutputStream("HungrySingleton");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(out);
objectOutputStream.writeObject(instance);
out.close();
objectOutputStream.close();
FileInputStream in = new FileInputStream(new File("HungrySingleton"));
ObjectInputStream objectInputStream = new ObjectInputStream(in);
HungrySingleton hungrySingleton = (HungrySingleton)
objectInputStream.readObject();
in.close();
objectInputStream.close();
System.out.println(instance);
System.out.println(hungrySingleton);
System.out.println(instance == hungrySingleton);
} catch (Exception e) {
e.printStackTrace();
}
}
解决方案,JDK在反序列化时会检测readResolve方法,通过复写方法防止新建实例。
/**
* 防止序列化破坏单例
* JDK反序列化时 使用反射调用无参构造函数创建实例
* @return
*/
public Object readResolve() {
return HUNGRY_SINGLETON;
}
网友评论