单例模式的书写
这里列出懒汉式的写法
/**在多线程下如何保证单例模式需要记住三点。
*第一就是双重加锁机制。
*第二是确保SingleTest为volatile变量。
*第三,是我觉得最容易被遗忘的一点,就是构造函数为private。
*/
public class SingleTest {
public static volatile SingleTest singleTest ;
private SingleTest(){
}
public static SingleTest getSingleTest(){
if(singleTest==null){
synchronized(SingleTest.class){
if(singleTest==null){
singleTest = new SingleTest();
}
}
}
return singleTest;
}
}
单例模式的破坏
上面的写法看起来没问题,但是存在安全问题。因为我们可以通过反射破坏单例模式。
public static void main(String[] args) throws Exception{
Constructor constructor = SingleTest.class.getDeclaredConstructor();
constructor.setAccessible(true);
SingleTest s1 = SingleTest.getSingleTest();
SingleTest s2 = SingleTest.getSingleTest();
SingleTest s3 = (SingleTest) constructor.newInstance();
System.out.println("输出结果为:"+s1.hashCode()+"," +s2.hashCode()+","+s3.hashCode());
}
输出结果为:1286131032,1286131032,1581347769
从输出的结果我们就可以看出s1和s2为同一对象,s3为新对象。s3是我们通过反射机制,进而调用了私有的构造函数,然后产生了一个新的对象。
诶,既然单例模式存在漏洞,在JDK中那些应用单例模式的对象不是就会被恶意破坏了嘛。当时我也这样想的,我尝试了一下去破坏Runtime(在JDK为单例模式对象)。
public static void main(String[] args) throws Exception{
Constructor constructor1 = Runtime.class.getConstructor();
Runtime r1 = (Runtime) constructor1.newInstance();
Runtime r2 = Runtime.getRuntime();
Runtime r3 = Runtime.getRuntime();
System.out.println(r1.hashCode());
System.out.println(r2.hashCode());
System.out.println(r3.hashCode());
}
结果是被异常抛出,查看源码就看得出,JDK对已经封装好的单例对象是有保护机制的。
防止单例模式的破坏
那么我们如何去保护我们自定义的单例对象呢?很简单,只需要修私有的构造函数即可。
private static boolean flag = false;
private SingleTest(){
synchronized(SingleTest.class){
if(flag == false){
flag = !flag;
}else {
throw new RuntimeException("单例模式被侵犯!");
}
}
}
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/MakeContral/article/details/78713458