懒汉模式:和饿汉模式不同,懒汉模式并不会一开始声明对象,而是需要等到调用时再声明对象。他很懒,所以你叫“它”它才会动...
代码1:
/** * 懒汉模式 */ public class LazybonesSingleton1 { //先声明 private static LazybonesSingleton instance; /** * 禁止外部构建 */ private LazybonesSingleton(){} /** * 对外提供调用方法 * @return */ public static LazybonesSingleton getInstance() { if(instance==null){ instance=new LazybonesSingleton(); } return instance; } /** * 测试 * @param args */ public static void main(String[] args) { for(int i=0;i<20;i++){ new Thread(()->{ System.out.println(LazybonesSingleton.getInstance()); }).start(); } } }
代码1 测试结果:发现 第一个线程运行时 和剩下线程运行 ,并不是一个单例。
这是因为多线程时,很有可能出现两个或多个线程同时执行。
也就是说他们同时运行到了 if(instance==null) 所以都创建了一个对象的实例
com.company.LazybonesSingleton@55477623 com.company.LazybonesSingleton@3996d457 com.company.LazybonesSingleton@3996d457 com.company.LazybonesSingleton@3996d457 com.company.LazybonesSingleton@3996d457 com.company.LazybonesSingleton@3996d457 com.company.LazybonesSingleton@3996d457 com.company.LazybonesSingleton@3996d457 com.company.LazybonesSingleton@3996d457 com.company.LazybonesSingleton@3996d457 com.company.LazybonesSingleton@3996d457 com.company.LazybonesSingleton@3996d457 com.company.LazybonesSingleton@3996d457 com.company.LazybonesSingleton@3996d457 com.company.LazybonesSingleton@3996d457 com.company.LazybonesSingleton@3996d457 com.company.LazybonesSingleton@3996d457 com.company.LazybonesSingleton@3996d457 com.company.LazybonesSingleton@3996d457 com.company.LazybonesSingleton@3996d457 Process finished with exit code 0
代码2:根据上述问题,我们进行了优化 getInstance 使用了 synchronized
/** * 懒汉模式 */ public class LazybonesSingleton2 { //先声明 private static LazybonesSingleton instance; /** * 禁止外部构建 */ private LazybonesSingleton(){} /** * 对外提供调用方法 * @return */ public static synchronized LazybonesSingleton getInstance() { if(instance==null){ instance=new LazybonesSingleton(); } return instance; } /** * 测试 * @param args */ public static void main(String[] args) { for(int i=0;i<20;i++){ new Thread(()->{ System.out.println(LazybonesSingleton.getInstance()); }).start(); } } }
代码2 测试结果:运行结果为同一个用例,保证了安全性。
但是这里出现了一个问题 synchronized 会让 线程变成串行(执行synchronized后,其他线程会在外进行等待,直到运行结束。再有下一个线程运行,剩下未执行的线程继续等待,同样直到该线程运行结束 )。
虽然保证了安全性,但是性能却很差。
com.company.LazybonesSingleton@3996d457。
com.company.LazybonesSingleton@3996d457
com.company.LazybonesSingleton@3996d457
com.company.LazybonesSingleton@3996d457
com.company.LazybonesSingleton@3996d457
com.company.LazybonesSingleton@3996d457
com.company.LazybonesSingleton@3996d457
com.company.LazybonesSingleton@3996d457
com.company.LazybonesSingleton@3996d457
com.company.LazybonesSingleton@3996d457
com.company.LazybonesSingleton@3996d457
com.company.LazybonesSingleton@3996d457
com.company.LazybonesSingleton@3996d457
com.company.LazybonesSingleton@3996d457
com.company.LazybonesSingleton@3996d457
com.company.LazybonesSingleton@3996d457
com.company.LazybonesSingleton@3996d457
com.company.LazybonesSingleton@3996d457
com.company.LazybonesSingleton@3996d457
com.company.LazybonesSingleton@3996d457
Process finished with exit code 0
代码3:我们再进行优化,将 synchronized 放到 if(instance==null) 里面
这样其他线程就不用在执行 getInstance() 时进行等待了,性能也有所改进。
/** * 懒汉模式 */ public class LazybonesSingleton3 { //先声明 private static LazybonesSingleton instance; /** * 禁止外部构建 */ private LazybonesSingleton(){} /** * 对外提供调用方法 * @return */ public static LazybonesSingleton getInstance() { if(instance==null){ synchronized (LazybonesSingleton.class){ instance=new LazybonesSingleton(); } } return instance; } /** * 测试 * @param args */ public static void main(String[] args) { for(int i=0;i<20;i++){ new Thread(()->{ System.out.println(LazybonesSingleton.getInstance()); }).start(); } } }
代码3 测试结果:虽然性能有所改进,但是依然存在安全问题,
原因在于:多线程运行过程中很有可能同时运行到 if(instance==null),虽然会在外面等待由另一个线程执行完再执行,所以也会存在创建多个实例的问题。
com.company.LazybonesSingleton@19281561 com.company.LazybonesSingleton@19281561 com.company.LazybonesSingleton@19281561 com.company.LazybonesSingleton@4f0c4707 com.company.LazybonesSingleton@19281561 com.company.LazybonesSingleton@19281561 com.company.LazybonesSingleton@19281561 com.company.LazybonesSingleton@19281561 com.company.LazybonesSingleton@19281561 com.company.LazybonesSingleton@19281561 com.company.LazybonesSingleton@19281561 com.company.LazybonesSingleton@19281561 com.company.LazybonesSingleton@19281561 com.company.LazybonesSingleton@19281561 com.company.LazybonesSingleton@19281561 com.company.LazybonesSingleton@19281561 com.company.LazybonesSingleton@19281561 com.company.LazybonesSingleton@19281561 com.company.LazybonesSingleton@19281561 com.company.LazybonesSingleton@19281561 Process finished with exit code 0
-----------------------------以下是我运行多次的结果-----------------------------------
com.company.LazybonesSingleton@4f0c4707
com.company.LazybonesSingleton@4f0c4707
com.company.LazybonesSingleton@4f0c4707
com.company.LazybonesSingleton@55477623
com.company.LazybonesSingleton@4f0c4707
com.company.LazybonesSingleton@4f0c4707
com.company.LazybonesSingleton@4f0c4707
com.company.LazybonesSingleton@4f0c4707
com.company.LazybonesSingleton@4f0c4707
com.company.LazybonesSingleton@4f0c4707
com.company.LazybonesSingleton@4f0c4707
com.company.LazybonesSingleton@4f0c4707
com.company.LazybonesSingleton@4f0c4707
com.company.LazybonesSingleton@4f0c4707
com.company.LazybonesSingleton@4f0c4707
com.company.LazybonesSingleton@4f0c4707
com.company.LazybonesSingleton@4f0c4707
com.company.LazybonesSingleton@4f0c4707
com.company.LazybonesSingleton@4f0c4707
com.company.LazybonesSingleton@4f0c4707
Process finished with exit code 0
代码4:在 synchronized 中 再判断一次是否为空,这样便能解决 代码3产生的问题。
此种方式 可以叫 双重检测(DCL),便能解决 懒加载的安全问题
/** * 懒汉模式 */ public class LazybonesSingleton { //先声明 private static LazybonesSingleton instance; /** * 禁止外部构建 */ private LazybonesSingleton(){} /** * 对外提供调用方法 * @return */ public static LazybonesSingleton getInstance() { if(instance==null){ synchronized (LazybonesSingleton.class){ if(instance==null){ instance=new LazybonesSingleton(); } } } return instance; } /** * 测试 * @param args */ public static void main(String[] args) { for(int i=0;i<20;i++){ new Thread(()->{ System.out.println(LazybonesSingleton.getInstance()); }).start(); } } }
代码4 运行结果:
com.company.LazybonesSingleton@19281561 com.company.LazybonesSingleton@19281561 com.company.LazybonesSingleton@19281561 com.company.LazybonesSingleton@19281561 com.company.LazybonesSingleton@19281561 com.company.LazybonesSingleton@19281561 com.company.LazybonesSingleton@19281561 com.company.LazybonesSingleton@19281561 com.company.LazybonesSingleton@19281561 com.company.LazybonesSingleton@19281561 com.company.LazybonesSingleton@19281561 com.company.LazybonesSingleton@19281561 com.company.LazybonesSingleton@19281561 com.company.LazybonesSingleton@19281561 com.company.LazybonesSingleton@19281561 com.company.LazybonesSingleton@19281561 com.company.LazybonesSingleton@19281561 com.company.LazybonesSingleton@19281561 com.company.LazybonesSingleton@19281561 com.company.LazybonesSingleton@19281561 Process finished with exit code 0