文章目录 1. 饿汉式 2. 懒汉式 3. DCL 双重校验锁懒汉式 4. 通过反射破坏DCL & 加锁阻止 5. 通过不调用 getInstance() 来破坏单例 6. 通过反射来干扰信号量,从而破坏单例 7. 通过枚举类实现单例,可以防止反射破坏单例 
 
学 JUC 的时候顺便摸了下单例模式,看的是狂神的JUC教程~ public  class  HungrySingleton  { private  byte [ ]  data1 =  new  byte [ 1024  *  1024 ] ; private  byte [ ]  data2 =  new  byte [ 1024  *  1024 ] ; private  byte [ ]  data3 =  new  byte [ 1024  *  1024 ] ; private  byte [ ]  data4 =  new  byte [ 1024  *  1024 ] ; private  static  HungrySingleton  INSTANCE =  new  HungrySingleton ( ) ; private  HungrySingleton ( )  { } public  static  HungrySingleton  getInstance ( )  { return  INSTANCE; } public  static  void  main ( String [ ]  args)  { System . out. println ( HungrySingleton . getInstance ( ) ) ; } 
} 
public  class  LazySingleton  { private  static  LazySingleton  INSTANCE =  null ; private  LazySingleton ( ) { } public  static  LazySingleton  getInstance ( ) { if ( INSTANCE ==  null ) { INSTANCE =  new  LazySingleton ( ) ; } return  INSTANCE; } 
} 
Double Check Lock,判断了两次,加了一个 synchronized 锁住代码块 为什么要判断两次:可能多个进程卡在 synchronized 锁这步,所以进去后还要再判断一次 不安全的原因:指令重排(见代码注释) 解决方法:加 volatile 关键字禁止指令重排(见第三行注释代码) public  class  DCLSingleton  { private  static  DCLSingleton  INSTANCE =  null ; private  DCLSingleton ( ) { } public  static  DCLSingleton  getInstance ( ) { if ( INSTANCE ==  null ) { synchronized  ( DCLSingleton . class ) { if ( INSTANCE ==  null ) { INSTANCE =  new  DCLSingleton ( ) ; } } } return  INSTANCE; } 
} 
从字节码指令角度,分析指令重排影响:(图源黑马JVM视频,侵删) 可以看到,JIT 编译器可能会优化,先 putstatic,把引用地址赋予 INSTANCE,然后再 Method 来初始化 但是,并发情况下,可能在赋予引用后,init 前,有其他线程访问了 getInstance(),获得了一个还未引用的 INSTANCE,导致出错。 可以通过反射 setAccessible 无视私有,使用构造器来破坏单例(见 main() 代码) 阻止方法:在构造器再加一个 synchronized 锁,并且进行反射破坏判断并抛出异常 public  class  TripleSingleton  { private  static  TripleSingleton  INSTANCE =  null ; private  TripleSingleton ( ) { synchronized  ( TripleSingleton . class ) { if ( INSTANCE !=  null ) { throw  new  RuntimeException ( "不要通过反射来破坏单例模式" ) ; } } } public  static  TripleSingleton  getInstance ( ) { if ( INSTANCE ==  null ) { synchronized  ( TripleSingleton . class ) { if ( INSTANCE ==  null ) { INSTANCE =  new  TripleSingleton ( ) ; } } } return  INSTANCE; } public  static  void  main ( String [ ]  args)  throws  Exception { System . out. println ( TripleSingleton . getInstance ( ) ) ; Constructor < TripleSingleton > =  TripleSingleton . class . getDeclaredConstructor ( null ) ; constructor. setAccessible ( true ) ; System . out. println ( constructor. newInstance ( ) ) ; } 
} 
既然 4 是基于 INSTANCE != null 的情况来判断,那么我们只要不理 INSTANCE 就可以破坏单例模式了~ 只通过反射得到的构造器来创造多个实例 解决方法:加一个信号量 flag,在构造函数中防止这种破坏方式 public  class  TripleSingleton2  { private  static  TripleSingleton2  INSTANCE =  null ; private  static  boolean  flag =  false ; private  TripleSingleton2 ( ) { synchronized  ( TripleSingleton2 . class ) { if ( flag ==  false ) { flag =  true ; } else  { throw  new  RuntimeException ( "不要通过反射来破坏单例模式" ) ; } } } public  static  TripleSingleton2  getInstance ( ) { if ( INSTANCE ==  null ) { synchronized  ( TripleSingleton2 . class ) { if ( INSTANCE ==  null ) { INSTANCE =  new  TripleSingleton2 ( ) ; } } } return  INSTANCE; } public  static  void  main ( String [ ]  args)  throws  Exception { Constructor < TripleSingleton2 > =  TripleSingleton2 . class . getDeclaredConstructor ( null ) ; constructor. setAccessible ( true ) ; System . out. println ( constructor. newInstance ( ) ) ; System . out. println ( constructor. newInstance ( ) ) ; } 
} 
只要通过反射来干扰信号量,就可以继续破坏单例模式了~(见 main() 代码) package  singletons ; import  java. lang. reflect.  Constructor ; 
import  java. lang. reflect.  Field ; 
public  class  TripleSingleton3  { private  static  TripleSingleton3  INSTANCE =  null ; private  static  boolean  flag =  false ; private  TripleSingleton3 ( ) { synchronized  ( TripleSingleton3 . class ) { if ( flag ==  false ) { flag =  true ; } else  { throw  new  RuntimeException ( "不要通过反射来破坏单例模式" ) ; } } } public  static  TripleSingleton3  getInstance ( ) { if ( INSTANCE ==  null ) { synchronized  ( TripleSingleton3 . class ) { if ( INSTANCE ==  null ) { INSTANCE =  new  TripleSingleton3 ( ) ; } } } return  INSTANCE; } public  static  void  main ( String [ ]  args)  throws  Exception { Field  flag =  TripleSingleton3 . class . getDeclaredField ( "flag" ) ; flag. setAccessible ( true ) ; Constructor < TripleSingleton3 > =  TripleSingleton3 . class . getDeclaredConstructor ( null ) ; constructor. setAccessible ( true ) ; TripleSingleton3  instance1 =  constructor. newInstance ( ) ; flag. set ( instance1,  false ) ; TripleSingleton3  instance2 =  constructor. newInstance ( ) ; System . out. println ( instance1 +  "\n"  +  instance2) ; } 
} 
为了进行反射破坏,先要获取 enum 类的构造器 观测源码,发现有无参构造函数,然而实际上这个是假的= =。 使用 javap 反编译,会发现还是有无参构造函数 使用 jad,会找到一个 private EnumSingle(String s, int i) 的构造函数,成功~ public  enum  EnumSingleton  { INSTANCE; public  static  void  main ( String [ ]  args)  throws  Exception { System . out. println ( EnumSingleton . INSTANCE) ; Constructor < EnumSingleton > =  EnumSingleton . class . getDeclaredConstructor ( String . class ,  int . class ) ; System . out. println ( constructor. newInstance ( ) ) ; } 
} 
会爆出这个错误: 更加深入可以去看看 Enum 的源码~