当前位置: 首页 > 后端技术 > Java

单例模式只有懒惰和饥饿?看完本文,面试狂加分

时间:2023-04-01 18:58:08 Java

前言说到设计模式,单例模式最有可能在面试中排在第一位。这一定是大部分人入门求职面试基础知识都绕不开的。但是单例模式不仅有懒人模式和饿汉模式两种写法,而往往我们掌握的是最基本的写法。如果你看过spring等知名框架的源码,你一定会发现它们的单例模式。你所拥有的是完全不同的。本文将从基础->最优->附加推荐给大家带来单例模式的写法,助你在面试中疯狂加分。懒汉与饿汉1.饿汉模式饿汉模式简单理解就是提前创建对象。优点:写法简单,没有线程同步问题缺点:因为需要提前创建对象,不管用不使用,都会一直占用内存建议:对象更小更简单,使用Hungry模式publicfinalclassSingleton{//创建一个好的实例privatestaticSingletoninstance=newSingleton();//构造函数privateSingleton(){}//获取实例publicstaticSingletongetInstance(){returninstance;}}2。懒人模式懒人模式简单理解就是只在需要的时候才创建对象。优点:懒加载方式性能更高缺点:要考虑多线程同步问题建议:只要不满足饿了么以上推荐条件都使用懒加载方式publicfinalclassSingleton{privatestaticSingletoninstance=无效的;//构造函数privateSingleton(){}//获取实例publicstaticSingletongetInstance(){//对象为null时实例化if(null==instance){instance=newSingleton();}返回实例;}}同步锁懒人的缺点就是多线程同步的问题,所以可以马上想到用同步锁来解决这个问题。这里使用了synchronized关键字,通过代码块降低锁粒度,最大程度保证了性能开销。其实从java8开始,synchronized的性能有了很大的提升。publicfinalclassSingleton{privatestaticSingletoninstance=null;//构造函数privateSingleton(){}//获取实例publicstaticSingletongetInstance(){//获取对象时加同步锁if(null==instance){synchronized(Singleton.class){instance=newSingleton();}}返回实例;}}Doublechecklock虽然上面使用了同步锁代码块,但是勉强解决了线程同步的问题,最大程度优化了性能开销。事实上,在多线程环境下仍然存在线程安全问题。当还有多个线程进入if判断时,这个线程安全问题依然存在。虽然这种情况不一定会发生,但在极端情况下发生的概率是非常高的。这时候就需要用到面试中喜欢问设计模式的DCL,即double-checklock模式。听起来很高端,其实多了一层判断力。说白了就是在进入同步锁之前和之后进行检查,这样就大大减少了线程安全问题。publicfinalclassSingleton{privatestaticSingletoninstance=null;//constructorprivateSingleton(){}//获取实例publicstaticSingletongetInstance(){//第一次判断,如果instance为null则实例化对象(null==instance){synchronized(Singleton.class){//第二次判断放入同步锁,当实例为null时,实例化对象if(null==instance){instance=newSingleton();}}}返回实例;}}最优双重检查锁双重检查锁方式是单例惰性模式处理多线程下安全问题的最佳解决方案之一,但以上仍然不是最好的写法。这里有一个“指令重排”的概念。这个概念在java内存模型中。在这里,我将以最简洁的方式帮助您理解它。在Java中,一个新对象在内存中的正常执行顺序是:分配->创建->引用,而在多线程环境下,JVM可能会重新安排语句优化的顺序:分配->引用->创建.如果出现这种情况,上面的双重检查加锁方式仍然不能解决线程安全问题。解决方法很简单,加个volatile关键字就可以了。volatile关键字的作用:保证可见性和顺序。publicfinalclassSingleton{//添加volatile关键字privatevolatilestaticSingletoninstance=null;//构造函数privateSingleton(){}//获取实例publicstaticSingletongetInstance(){//首先判断,当instance为null时,实例化对象if(null==instance){synchronized(Singleton.class){//第二个判断放在同步锁中。当实例为null时,实例化对象if(null==instance){instance=newSingleton();}}}返回实例;}}枚举模式《Effective Java》是Java界非常火的一本书。没有理由不去读它。相信很多Java程序员不管是否读过这本书,都听说过这本书。本书作者推荐一种单例设计模式,就是枚举法。原理很简单。在Java中,枚举类的域在编译后会声明为静态属性,JVM会保证静态修饰的成员变量只被实例化一次。publicclassSingleton{//构造函数privateSingleton(){}//从枚举中获取实例publicstaticSingletongetInstance(){returnSingletonEnum.SINGLETON.getInstance();}//定义枚举privateenumSingletonEnum{SINGLETON;私有单例实例;//JVM保证这个方法只被调用一次SingletonEnum(){instance=newSingleton();}publicSingletongetInstance(){返回实例;感觉有点负担。单例模式其实很简单。饿鬼模式和懒人模式在很多开源框架中被广泛使用,甚至饿鬼模式用得更多。例如,这是在Java的Runtime类中完成的。简单粗暴。有兴趣的可以自己看看源码。这些框架的作者难道没有意识到本文描述的问题吗?不会的,单例模式用什么方式写,往往要视情况而定。有些理论上会出现的问题,在实践中往往可以忽略不计。这时候更倾向于用最简单直接的写法。真正难的其实是面试。很多关于单例模式的问题都喜欢问它的几种写法,存在的问题和最佳解决方案。对设计模式的理解程度,以此来判断你对这门学科的研究态度和造诣。因此,看完这篇文章,你可以尝试手动编写。懂点就够了,没必要钻得太深,因为Java领域需要精力的地方确实太多了。体验最后说一下我的体验吧。所谓设计模式固然可以给Java代码本身带来更多的优雅,但是写了很多年Java代码,我还是觉得Java本身有太多的装饰,优雅的交换往往是代码本身的负担。在我参与过的研发团队中,几乎可以看到很多工程师写的比较优雅的代码,一些设计模式也写的很好,但是可能造成的问题也很明显,就是可读性越来越差,更差。每个团队成员在Java方面都有很高的造诣,甚至在某些时候对人力资源施加压力,从实际角度来看是不合适的。我更多的建议是,面对面试或者学习,了解设计模式是有好处的,但是在实际工作中,尽量使用不那么复杂的设计模式,以简洁直接的代码为主,有利于后期维护整个团队。甚至加快了新成员对人事变动后项目的适应,因为工作还是以绩效为导向,简单高效什么都可以,个人项目随心所欲发挥。我的原创文章纯属手写。如果觉得有一点帮助,请点个赞加收藏吧~我一直在分享自己在工作中的感悟、经验和实践案例。喜欢的话也可以进入个人主页关注哦~