单例模式是一种经常使用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。经过单例模式能够保证系统中一个类只有一个实例。设计模式
早晨,老王来到公司,发现小蔡正对着电脑屏幕发呆,因而走到小蔡身后,发现小蔡正对着一个宝宝照发呆。安全
老王拍了一下小蔡肩膀,问:“这个是谁啊?没据说你有男友啊。难道是私生子?”多线程
小蔡回头,呸了老王一下,说:“这是我二表姑的侄子的姐夫的姑姑的妹妹的舅舅的儿媳妇的堂哥的娃娃。”并发
老王还没回过神来,小蔡又说:“宝宝真可爱,要是我之后要宝宝了,我必定只要一个,我要带她处处去耍,给他买各类漂亮的衣服,各类好玩的玩具,各类吧嗒吧嗒吧嗒……(此处省略数千字)”函数
老王心想:“都说一个女人等于500只鸭子,这个小蔡何止500只鸭子,简直就是5000只鸭子,并且全是话痨型鸭子。”spa
老王趁小蔡两句话之间喘气的时机,赶忙打断了小蔡:“你说到只要一个宝宝,我给你讲讲程序代码只要一个宝宝的方法,好不?”线程
小蔡一听,很好奇:“程序代码,也有一个宝宝的说法?”设计
老王说眼看将话题转移了,赶忙接着说:“是啊,那就是单例模式,它可让整个程序系统里,只存在惟一的实例对象。从而大幅节省内存资源。”代理
老王不给小蔡搭话的机会,紧接着演示起了代码:“单例模式,分为懒汉式和饿汉式,咱们先来看看懒汉式。”code
//懒汉式 public class Lazybones { //实例对象 private static Lazybones instances = null; //私有的构造函数,阻止实例化对象 private Lazybones(){} //若是发现没有实例对象,就构造一个 //若是有实例对象,直接返回 public static Lazybones getInstances(){ if(instances == null){ instances = new Lazybones(); } return instances; } }
小蔡很好奇,问:“代码也有懒汉?”
老王说:“对啊,在使用到的时候才实例化,这就叫懒汉式。这种方式有个好处就是在不使用的时候不会占用内存空间。可是这里也有一个问题。”
小蔡问:“什么问题啊?”
老王说:“在大并发,多线程的环境下,假若有多个线程同时执行到getInstances()
方法,第一个线程执行if
语句,尚未完成构造时,第二个线程也执行到if
这里,这时候instance
依然为空。这样线程一和线程二会同时产生两个实例。因此懒汉式的单例模式,并非线程安全的 。”
小蔡又问:“难道饿汉式是线程安全的?”
老王说:“对的,饿汉式单例模式的确是线程安全的。我们来看代码。”
//饿汉式 public class Hungry { //无论三七二十一,直接实例化一个对象 private static Hungry instances = new Hungry(); //私有的构造函数,阻止实例化对象 private Hungry(){} //直接返回已经实例化了的对象 public static Hungry getInstances(){ return instances; } }
老王说:“你看,饿汉式单例模式,无论是否使用,直接就将对象实例化在那儿放着,要用的时候直接使用,这样就不用再去判断,因此饿汉式单例模式是线程安全的。”
小蔡问:“老王,咱们怎么验证这个这个对象是否单例呢?咱们又看不到内存里的分配状况。”
老王说:“这个咱们就得靠对象的哈希码了。由于哈希码是用来在散列存储结构中肯定对象的存储地址的。简单理解,就是一个对象的hash code和内存地址是一一对应的,只要hash code值相同,那么就是同一内存地址。在Java里,一个对象的toString()
方法默认返回这个对象的hash code。咱们来看代码。”
public class SingleCheck { public static void main(String[] args) { //经过饿汉式单例模式,得到3个对象 Hungry h1 = Hungry.getInstances(); Hungry h2 = Hungry.getInstances(); Hungry h3 = Hungry.getInstances(); //打印其Hash code System.out.println("h1:" + h1); System.out.println("h2:" + h2); System.out.println("h3:" + h3); //经过懒汉式单例模式,得到3个对象 Lazybones l1 = Lazybones.getInstances(); Lazybones l2 = Lazybones.getInstances(); Lazybones l3 = Lazybones.getInstances(); //打印其Hash code System.out.println("l1:" + l1); System.out.println("l2:" + l2); System.out.println("l3:" + l3); } }
其结果为:
老王说:“从结果,咱们能够看到,经过getInstances()
方法获得的对象,不管获取多少次,的确为同一对象。这里我再说一下单例模式的关键点:1. 私有的构造函数,防止从外部构造对象。2. 静态的私有变量存储对象。3.共开的获取对象的方法。经过这三点,就能够完成单例模式的编写,经过运用单例模式,咱们能够避免重复构造对象,从而节省系统的内存资源消耗。”
小蔡说:“老王,这就是你说的只有一个宝宝的代码?”
老王说:“对啊。正是。”
小蔡眨了眨眼,又问:“那若是我不止想要一个宝宝呢?如今国家政策放开了,能够要两个宝宝了。”
老王说:“那你就得先找老公了呀。”
小蔡怒道:“我问的是代码!只要2个宝宝的代码!”
老王笑道:“那就是多例模式,咱们下回再讲。”
更多内容,正在赶来,敬请关注“小蔡和老王的技术平常”。
PS:小蔡和老王的技术平常,已经创建QQ群,QQ群号:261434596,欢迎加入。