单例模式(Singleton)是一种非常简单且容易理解的设计模式。顾名思义,单例即单一的实例,确切地讲就是指在某个系统中只存在一个实例,同时提供集中、统一的访问接口,以使系统行为保持协调一致
package com.cyc.mystudy.singleton; /** * @Author cyc * @create 2022/7/30 11:24 */ public class Test01 { private static final Test01 INSTANCE=new Test01(); private Test01(){ } public static Test01 getINSTANCE() { return INSTANCE; } public static void main(String[] args) { for (int i=0;i<1000;i++){ new Thread(()->{ System.out.println(Test01.getINSTANCE().hashCode()); }).start(); } } }
私有的构造方法使得Test01完全被封闭起来 实例化工作是自己内部的事务
private static final 修饰 保证了 INSTANCE是私有的 ,不可见的不可访问的,static保证了静态性,在类被加载进内存时,就已经初始化 ,final保证INSTANCE是常量,是不能被修改的
外部只要调用公共的方法TEST01.getINSTANCE就可以获得唯一的实例对象了
package com.cyc.mystudy.singleton; /** * @Author cyc * @create 2022/7/30 11:24 */ public class Test03 { private static final Test03 INSTANCE; static { INSTANCE=new Test03(); } private Test03(){ } public static Test03 getINSTANCE() { return INSTANCE; } public static void main(String[] args) { for (int i=0;i<1000;i++){ new Thread(()->{ System.out.println(Test03.getINSTANCE().hashCode()); }).start(); } } }
此处将实例化操作放到静态代码块中
package com.cyc.mystudy.singleton; /** * @Author cyc * @create 2022/7/30 11:33 */ public class Test02 { private static Test02 test02; private Test02(){}; public static Test02 getInstance(){ if (test02==null){ test02=new Test02(); } return test02; } public static void main(String[] args) { for (int i=0;i<1000;i++){ new Thread(()->{ System.out.println(Test02.getInstance().hashCode()); }).start(); } } }
恶汉模式如果没人使用,但是却实例化对象 ,这样一块内存区不是白浪费了 这样单杀了懒汉模式的写法
只有当某一个线程第一次调用getINSTANCE时才会进行实例化操作 之后再有线程访问直接返回对象
这样程序乍看确实没什么问题 但是在多线程环境下 可能会有多个线程进入到了getINSTANCE方法内,这样就会导致原来已经实例化的对象被覆盖掉
为了保证线程安全 我们给getINSTANCE方法加上 synchronized同步锁 下面看第四种写法
package com.cyc.mystudy.singleton; /** * @Author cyc * @create 2022/7/30 11:33 */ public class Test04 { private static Test04 test02; private Test04(){}; public static synchronized Test04 getInstance(){ if (test02==null){ test02=new Test04(); } return test02; } public static void main(String[] args) { for (int i=0;i<1000;i++){ new Thread(()->{ System.out.println(Test04.getInstance().hashCode()); }).start(); } } }
这样确实没有什么问题 然而这样的做法是要付出一定代价的,试想,线程还没进入方法内部便不管三七二十一直接加锁排队,会造成线程阻塞,资源与时间被白白浪费。我们只是为了实例化一个单例对象而已,犯不上如此兴师动众,使用synchronized让所有请求排队等候。所以,要保证多线程并发下逻辑的正确性,同步锁一定要加得恰到好处
下面看第五种写法 在方法体内部加锁:
package com.cyc.mystudy.singleton; /** * @Author cyc * @create 2022/7/30 11:33 */ public class Test05 { private static Test05 test02; private Test05(){}; public static Test05 getInstance(){ if (test02==null){ synchronized (Test05.class){ test02=new Test05(); } } return test02; } public static void main(String[] args) { for (int i=0;i<1000;i++){ new Thread(()->{ System.out.println(Test05.getInstance().hashCode()); }).start(); } } }
这样在多线程环境也会有一定问题 ,可能会有多个线程同时通过了 tese02==null 的判断进入了方法里,这样也会造成重复的实例化
package com.cyc.mystudy.singleton; /** * @Author cyc * @create 2022/7/30 11:33 */ public class Test06 { private static volatile Test06 test02; private Test06(){}; public static Test06 getInstance(){ if (test02==null){ synchronized (Test06.class){ if (test02==null){ test02=new Test06(); } } } return test02; } public static void main(String[] args) { for (int i=0;i<1000;i++){ new Thread(()->{ System.out.println(Test06.getInstance().hashCode()); }).start(); } } }
我们一共用了2个嵌套的判空逻辑,这就是懒加载模式的“双检锁”:外层放宽入口,保证线程并发的高效性;内层加锁同步,保证实例化的单次运行。如此里应外合,不仅达到了单例模式的效果,还完美地保证了构建过程的运行效率,一举两得。
package com.cyc.mystudy.singleton; /** * @Author cyc * @create 2022/7/30 11:51 */ public class Test07 { private Test07(){}; private static class Test0701{ private static final Test07 test07=new Test07(); } public static Test07 getInstance(){ return Test0701.test07; } public static void main(String[] args) { for (int i=0;i<1000;i++){ new Thread(()->{ System.out.println(Test07.getInstance().hashCode()); }).start(); } } }
package com.cyc.mystudy.singleton; /** * @Author cyc * @create 2022/7/30 11:57 */ public enum Test08 { INSTANCE; public void m(){ System.out.println("业务代码"); } public static void main(String[] args) { Test08.INSTANCE.m(); } }
在一般情况下我们使用饿汉模式,恶汉模式不用担心多线程环境会出问题,写法上也比较简单,
我们不用为了省一点性能而去给自己造成麻烦