java最常见设计模式之单例模式剖析

  前言:

     单例模式是java中最常见的设计模式之一, 在j2EE生态圈的众多第三方框架中运用,如springIOC容器的单例管理。那么我们 为什么需要使用单例模式呢,从字面意思来看单例即只有一个实例. 一个实例必然占用的内存资源就小,减少了内存的开销。那么必然,有利则必有弊,单例必然是线程不安全的!(解释一下什么是线程安全:线程相当于人,高并发的宏观表现即:相当于多个人在不互相交流同时去完成一项任务,比如发传单,A已经给这个地方的人发过了,而B不知道A已经给这个地方的人发过了,又发了一遍!那么就造成了重复,本来100张传单需要发送给100个人,那么现在有可能100张传单只发给了50个人。)由此可见,当你的类中不包含对公有数据的操作的话(如成员变量或类变量),那么即可以使用单例模式!这在面试中也会被经常问到,我们为什么需要使用单例?希望这篇文章对您有所帮助!

   话不多说进入正题,下面讲解三种单例模式

    (1).饿汉式(什么都不想做就想直接吃,这里以线程的角度来看的话就是直接拿,对线程来说我进来你这个方法,什么都不想做,就想直接得到对象)

            解释一下上面为什么是单例的,因为static标识的都是静态的公有的是类的属性或方法,根据双亲委派模型(双亲委派模型就是,jvm有好几种类加载器,每种类加载器加载类的范围不一样,而低级的类加载器在得到类加载任务的时候会将这个任务往它的最上层加载器传,当最上层加载器判断这个类型不再我的加载范围的时候再低级的类加载器传,这样就回保证类只会被加载一次),在程序运行后,类只会被加载一次,故类对象只有一个。

   (2).懒汉式(必然有个线程需要去new一个实例,而其他线程在进来的时候就可以直接得到,这就相当于大家都懒,都想让别人去做,自己坐享其成)


         这里用到了synchronized同步操作,如果不加的话会造成高并发情况下会有多个线程去new 这个对象,这样虽然最后也可以达到单例的效果但是不停的new对象是十分消耗系统性能的,所以需要加同步控制(注意:这里我加了一个类对象的锁,这种锁相当于数据库的悲观锁,因为他相当于将整个类关于同步资源的争夺都给锁住了,只有当一个线程做完后,本对象或者其他本类下的实例对象的线程进来跑的时候在可以再争夺锁)!这样的话就回保证只有一个线程来执行new的操作,其他线程坐享其成直接用就可以了!但是这里又有一个问题锁的竞争也是很严重的,也非常消耗性能,下面的例子是对这种的改进!

  (3).DLC(双重检查)既然上面说到了既然对象已经被new了,我们就没有必要再去锁里面了,这样需要给外层加层判断!即就是双重检查!


     这样写的话就可以避免我们已经new出了对象还要跑去竞争锁,拼的你死我活的得到锁进去,又不做什么,徒劳无获!

这里解释一下为什么这里在静态变量上加了一个volatile这个关键字。对象在new的时候需要经历3个过程:

         1.向内存申请空间

            2.对象进行初始化

              3.给变量赋引用

  需要注意的是这三个步骤jvm在执行的时候是可能会不按照这样的顺序进行,可能会发生重排序,用户期待的是1-2-3的执行顺序,而jvm可能进行的是1-3-2的顺序,那么这样造成的必然是对象都没有被初始化在经历第一次非空判断的时候就会被直接返回,因为此时你的变量引用不为空,只不过是对象还没有被构造好,这样就会造成返回的对象在进行调用对象的时候进行报错。所有我们需要告知jvm你不能帮我重排序,而volatile这个关键字就是干这个活的!这个单例的实现相当于第二种的话在锁的竞争上就减轻了,虽然前面构造的时候不会减轻,但是一旦构造好,则不会去竞争锁了。

    综上三种方法,做理性的判断饿汉式性能应为最好,但是实际开发中,为了装逼,显得自己有水平,还是会采用第三种方式的写法!     

     最后希望大手跳过勿喷!此文谨供新手学习或对此模式理解不到位的加强。

     我衷心祝愿大家在java学习中品尝java(咖啡)带来的香醇和享受!



      

             



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值