1、基础入门单例:
1 public class Singleton { 2 private Singleton(){ 3 System.out.println("Singleton 初始化的过程"); 4 } 5 6 private static Singleton singleton = null; 7 8 /**@Description: 单例同步控制,方法加syn 9 * @return 10 * @author BIQI 2017年12月20日下午3:10:23 11 * @return Singleton @throws 12 */ 13 public static synchronized Singleton getSingleton1(){ 14 if (null == singleton ) { 15 singleton = new Singleton(); 16 } 17 return singleton; 18 } 19 20 /**@Description: 单例同步控制2,使用ReentrantLock锁 21 * @return 22 * @author BIQI 2017年12月20日下午3:14:16 23 * @return Singleton @throws 24 */ 25 public static Singleton getSingleton2(){ 26 ReentrantLock lock = new ReentrantLock(); 27 lock.lock(); 28 if (null == singleton ) { 29 singleton = new Singleton(); 30 } 31 lock.unlock(); 32 return singleton; 33 } 34 35 /**@Description: 双重检测机制 DCL(Double CheckLock)实现单例 36 * @return 37 * @author BIQI 2017年12月20日下午3:15:31 38 * @return Singleton @throws 39 */ 40 public static Singleton getSingleton3(){ 41 //第一重检测 42 if (null == singleton ) { 43 synchronized (singleton) { 44 //第二重检测 45 if (null == singleton) { 46 singleton = new Singleton(); 47 } 48 } 49 } 50 return singleton; 51 } 52 }
2、单例的进阶,控制指令重排
public class Singleton2 { /* * 指令重排是 比如java中简单的一句 instance = new Singleton,会被编译器编译成如下JVM指令: memory =allocate(); //1:分配对象的内存空间 ctorInstance(memory); //2:初始化对象 instance =memory; //3:设置instance指向刚分配的内存地址 但是这些指令顺序并非一成不变,有可能会经过JVM和CPU的优化,指令重排成下面的顺序: memory =allocate(); //1:分配对象的内存空间 instance =memory; //3:设置instance指向刚分配的内存地址 ctorInstance(memory); //2:初始化对象 */ private Singleton2() { System.out.println("Singleton 初始化的过程"); } //volatile 阻止变量访问前后指令的重排,保证指令执行顺序 private volatile static Singleton2 singleton = null; //单例对象 //静态工厂方法 public static Singleton2 getInstance() { if (singleton == null) { //双重检测机制 synchronized (Singleton.class){ //同步锁 if (singleton == null) { //双重检测机制 singleton = new Singleton2(); } } } return singleton; } }
3、classloader的加载机制来实现懒加载单例实现
1 public class Singleton3 { 2 /** 3 * 1.从外部无法访问静态内部类LazyHolder, 4 * 只有当调用Singleton.getInstance方法的时候, 5 * 才能得到单例对象INSTANCE。 6 * 2.INSTANCE对象初始化的时机并不是在单例类Singleton被加载的时候, 7 * 而是在调用getInstance方法,使得静态内部类LazyHolder被加载的时候。 8 * 因此这种实现方式是利用classloader的加载机制来实现懒加载, 9 * 并保证构建单例的线程安全。 10 * 11 */ 12 private static class LazyHolder { 13 private static final Singleton3 INSTANCE = new Singleton3(); 14 } 15 private Singleton3 (){} 16 public static Singleton3 getInstance() { 17 return LazyHolder.INSTANCE; 18 } 19 }
4、最推荐的单例,使用枚举,因为枚举的特性以及实现方式(这里不阐述);
常量实际是被编译为静态变量,Java中静态变量都是存储在Method Area
1 public enum SingletonEnum1 { 2 3 INSTANCE(); 4 5 SingletonEnum1(){ 6 System.out.println("SingletonEnum1 初始化的过程"); 7 } 8 }
5、如何破坏单例模式,如果不适用枚举的话;
1 /** 2 * @Title: BreakSingleton.java 3 * @Description: 破坏单例的情况,后面通过枚举去破解 4 * 5 * @author BIQI IS BEST 6 * @date 2017年12月20日 下午3:27:39 7 * @version V1.0 8 */ 9 public class BreakSingleton { 10 11 public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { 12 // TODO Auto-generated method stub 13 14 //获得构造器 15 Constructor con = Singleton.class.getDeclaredConstructor(); 16 //设置为可访问 17 con.setAccessible(true); 18 //构造两个不同的对象 19 Singleton singleton1 = (Singleton)con.newInstance(); 20 Singleton singleton2 = (Singleton)con.newInstance(); 21 //验证是否是不同对象 22 System.out.println(singleton1.equals(singleton2)); 23 24 //枚举类的获得 25 SingletonEnum1 singletest1 =SingletonEnum1.INSTANCE; 26 SingletonEnum1 singletest2 =SingletonEnum1.INSTANCE; 27 System.out.println(singletest1.equals(singletest2)); 28 } 29 }