原创

双重检查单例模式的破坏与枚举单例模式


双重检查单例模式

双重检查单例模式可以很好的解决多线程环境下 线程切换带来的原子性,编译优化带来的有序性问题,可以说是“安全”的单例模式。

双重检查单例模式如下:

class Singleton {
        private Singleton() {
        }
    static Singleton singleton;
public static Singleton getSingleton() {
    if (singleton == null) {
        synchronized (Singleton.class) {
            if (singleton == null) {
                singleton = new Singleton();
            }
        }
    }
    return singleton;
}

}

一般来讲,双重检查单例模式是比较安全的。但是其实并非绝对安全,我们可以通过反射的方式对单例模型进行破坏从而获取到多个实例对象。

public class Main {
        public static void main(String[] args) throws Exception {
            Singleton singletonOne = Singleton.getSingleton();
            Singleton singletonTwo = Singleton.getSingleton();
            System.out.println("One和Two是否是同一个实例? " + (singletonOne == singletonTwo));
            //利用反射来破坏单例模式
            Class clazz = Class.forName("Singleton");
            Constructor constructor = clazz.getDeclaredConstructor(null);
            constructor.setAccessible(true);
            Singleton singletonThree = (Singleton) constructor.newInstance(null);
            System.out.println("One和three是否是同一个实例? "+(singletonOne==singletonThree));
        }
    }

输出结果为True和False;

在此我们通过反射的方式破坏了经过双重检查的单例模式。为此我们可以采用枚举单例模式进一步提高安全性。

枚举单例模式

Effective Java作者Josh Bloch 提倡使用枚举的方式,因为创建一个enum类型是线程安全的。这种方法在功能上与公有域方法相近,但是它更加简洁,无偿提供了序列化机制,绝对防止多次实例化,即使是在面对复杂序列化或者反射攻击的时候。

public class SingleTest{
	private SingleTest(){}

	public static SingleTest getInstance(){
    	return SingleTon.Single.getInstance();
	}


	private enum SingleTon{
    	Single;
    
		private SingleTest singleTest;
    
    	SingleTon(){
        	singleTest = new SingleTest();
    	}
    
    	public SingleTest getInstance(){
        	return singleTest;
    	}
	}
}
Java
设计模式
多线程

评论