单例的应用场景
- 单例类只有一个实例对象;
- 该单例对象必须由单例类自行创建;
- 单例类对外提供一个访问该单例的全局访问点。
常见的使用场合:数据库的连接池、Spring中的全局访问点BeanFactory,Spring下的Bean、多线程的线程池、网络连接池等等
- 频繁创建的一些类,又频繁被销毁
- “昂贵的对象”,有些对象创建的成本比其他对象要高得多,比如占用资源较多,或实例化耗时较长
- 系统要求单一控制逻辑的操作,或者对象需要被共享的情况
- ......
不仅可以减少每次创建对象的时间开销,还可以节约内存空间;缺点:
能够避免由于操作多个实例导致的逻辑错误;
如果一个对象有可能贯穿整个应用程序,能够起到了全局统一管理控制的作用。
单例模式一般没有接口,没有抽象层,扩展困难。如果要扩展,得修改原来的代码单例的实现方式
单例模式的功能代码通常写在一个类中,其职责过重,如果功能设计不合理,则很容易违背单一职责原则
不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误,不能保存彼此的状态。比如单例模式下去将对象转成json 会出现互相引用的问题 。
原子操作就是不可分割的操作,在计算机中,就是指不会因为线程调度被打断的操作。而在我们现代的计算机中CPU是乱序执行。CPU的速度是超级快的,但同时其价格也是非常昂贵的。为了"充分"压榨CPU, 我们要把CPU的时间进行分片,让各个程序在CPU上轮转,造成一种多个程序同时在运行的假象,即并发。
并发是针对单核 CPU 提出的,而并行则是针对多核 CPU 提出的。和单核 CPU 不同,多核 CPU 真正实现了“同时执行多个任务”在CPU中为了能够让指令的执行尽可能地同时运行起来,采用了指令流水线。一个 CPU 指令的执行过程可以分成 4 个阶段:取指、译码、执行、写回。这 4 个阶段分别由 4 个独立物理执行单元来完成。理想的情况是:指令之间无依赖,可以使流水线的并行度最大化
所谓指令重排是指在不改变原语义的情况下,通过调整指令的执行顺序让程序运行的更快。JVM中并没有规定编译器优化相关的内容,也就是说JVM可以自由的进行指令重排序的优化。
无论是编译期的指令重排还是** CPU 的乱序执行**,主要都是为了让 CPU 内部的指令流水线可以“填满”,提高指令执行的并行度。指令重排对于非原子性的操作,在不影响最终结果的情况下,其拆分成的原子操作可能会被重新排列执行顺序。instance = new SingleDoubleCheck()的操作1234可能变成1243。这样会存在一个instance已经不为null但是SingleDoubleCheck仍没有完成初始化的状态这个时候其他的线程过来,走到part 1 if (instance == null)处时会产生:明明instance不为空,但是SingleDoubleCheck却没有的问题
-server
-Xcomp
-XX:+UnlockDiagnosticVMOptions
-XX:+PrintAssembly
-XX:CompileCommand=compileonly,*SingleVolatile.getInstance
VM参数我贴了一下,大家感兴趣可以去试试
-server它的汇编代码,我们根据'putstatic'同样截取一段:
-Xcomp
-XX:+UnlockDiagnosticVMOptions
-XX:+PrintAssembly
-XX:CompileCommand=compileonly,*SingleDoubleCheck.getInstance
欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) | Powered by Discuz! X3.4 |