双重检查模式 以双重检查模式为例
1 2 3 4 5 6 7 8 9 10 11 12 13 public class Singleton { private static Singleton singleton; public static Singleton getSingleton(){ if (singleton==null ){ synchronized (Singleton.class ){ if (singleton==null ){ singleton=new Singleton(); } } } return singleton; } }
最常见的单例实现,但是这个单例实现有三个问题
问题一 虽然已经使用synchronized进行同步,但在第4步创建对象时,会有下面的伪代码:
1 2 3 memory =allocate()ctorInstance() singleton =memory
当线程A在执行上面伪代码时,2和3可能会发生重排序,因为重排序并不影响运行结果,还可以提升性能,所以JVM是允许的。如果此时伪代码发生重排序,步骤变为1->3->2,线程A执行到第3步时,线程B调用getsingleton
方法,在判断singleton==null
时不为null,则返回singleton
。但此时singleton
并还没初始化完毕,线程B访问的将是个还没初始化完毕的对象。当声明对象的引用为volatile
后,2、3步会被禁止重排序
问题二 这种代码不能防止被直接创建 所以要添加私有构造函数
问题三 即使构造函数私有化,也不能防止被反射攻击 比如
1 2 3 4 5 6 7 8 9 public class Test { public static void main(String [] args) throws Exception{ Singleton s1=Singleton.getSingleton(); Constructor<Singleton> constructor =Singleton.class.getDeclaredConstructor( ); constructor .setAccessible(true ); Singleton s2=constructor .newInstance( ); System.out.println(s1==s2 ); } }
输出为false
其中constructor.setAccessible(true);
调用方法或者产生初始化对象实例的时候会践行权限检查 ,这个问题解决思路就是防止私有的构造方法被创建两次
1 2 3 4 5 6 7 8 9 10 private static boolean flag=true ; private Singleton () { synchronized (Singleton.class){ if (flag){ flag=false ; }else { throw new RuntimeException("对象已存在" ); } } }
再一次使用反射代码测试,输出结果
1 2 3 4 5 6 7 8 9 Exception in thread "main" java.lang .reflect .InvocationTargetException at sun.reflect .NativeConstructorAccessorImpl .newInstance0 (Native Method) at sun.reflect .NativeConstructorAccessorImpl .newInstance (NativeConstructorAccessorImpl.java :62 ) at sun.reflect .DelegatingConstructorAccessorImpl .newInstance (DelegatingConstructorAccessorImpl.java :45 ) at java.lang .reflect .Constructor .newInstance (Constructor.java :423 ) at test.test .Test .main (Test.java :10 ) Caused by: java.lang .RuntimeException : 对象已存在 at test.test .Singleton .<init>(Singleton.java :11 ) ... 5 more
完整双重检查模式代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class Singleton { private static volatile Singleton singleton; private static boolean flag=true ; private Singleton(){ synchronized (Singleton.class ){ if (flag){ flag=false ; }else { throw new RuntimeException("对象已存在" ); } } } public static Singleton getSingleton(){ if (singleton==null ){ synchronized (Singleton.class ){ if (singleton==null ){ singleton=new Singleton(); } } } return singleton; } }