五分钟精通设计模式--单例模式
Posted IT殿堂
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了五分钟精通设计模式--单例模式相关的知识,希望对你有一定的参考价值。
本文由【程序员伟哥】原创,转载请注明出处,不注明请默念三遍【我爱技术】后转载! 更多精彩请关注微信公众号:【IT殿堂】 可以加我微信一起交流技术:【qqqq123456】
都说单例模式简单,可是简单中藏着大大的不简单。
一、介绍
为了避免某个频繁使用的对象不断地重新创建,我们可以使用单例模式。单例模式可以保证在一个JVM中,只有一个实例存在。
二、实现步骤
1、创建单例类
2、在单例类中,声明 private static 的这个类本体类型的变量。
3、只在这个类中保留private的构造方法
4、写一个静态方法,获取这个类被本体类型变量持有的静态变量实例(若为null需要新建)。
如下图所示:
//第一步,创建单例类 public class Singleton { //第三步、只在这个类中保留private的构造方法 private Singleton(){} //第二步、在单例类中,声明 private static 的这个类本体类型的变量。 private static Singleton singleton = null; //写一个静态方法,获取这个类被本体类型变量持有的静态变量实例(若为null需要新建)。 public static Singleton getInstance(){ if(singleton == null){ singleton = new Singleton(); } return singleton; } }
哇塞,单例模式 果然简单,到此万事大吉了!
那么你太天真了!
没错,这个类可以满足基本要求,但是,像这样毫无线程安全保护的类,如果我们把它放入多线程的环境下,很可能就出现问题了!
写一个Test类,如下:
/** * @Title Test.java * @Package com.buckyball.dp.singleton * @author Roy * @email 574613441@qq.com * @weixin qqqq123456 * @date 2018年6月28日 下午8:09:06 * @Desc */ package com.buckyball.dp.singleton; /** * @Title Test.java * @Package com.buckyball.dp.singleton * @author Roy * @email 574613441@qq.com * @weixin qqqq123456 * @date 2018年6月28日 下午8:09:06 * @Desc */ public class Test { public static Singleton ssingleton; /** * @param args */ public static void main(String[] args) { Thread thread1 = new Thread(new Runnable(){ @Override public void run() { System.out.println("thread1 run"); Singleton singleton = Singleton.getInstance(); System.out.println("thread1 singleton = "+singleton); ssingleton = singleton; } }); thread1.setName("thread1"); Thread thread2 = new Thread(new Runnable(){ @Override public void run() { System.out.println("thread2 run"); Singleton singleton = Singleton.getInstance(); System.out.println("thread2 singleton = "+singleton); ssingleton = singleton; } }); thread2.setName("thread2"); Thread thread3 = new Thread(new Runnable(){ @Override public void run() { System.out.println("thread3 run"); Singleton singleton = Singleton.getInstance(); System.out.println("thread3 singleton = "+singleton); ssingleton = singleton; } }); thread3.setName("thread3"); thread1.start(); thread2.start(); thread3.start(); } }
连续运行两次运行输出的结果如下:很明显,如果有并发线程同时调用获取单例,那么是否获取到的是同一单例,那么就看天意了!显然作为一个靠谱的程序是不能这么做的。
thread1 run thread2 run thread3 run thread1 singleton = com.buckyball.dp.singleton.Singleton@13c339fthread2 singleton = com.buckyball.dp.singleton.Singleton@ba8af9thread3 singleton = com.buckyball.dp.singleton.Singleton@ba8af9
thread1 run thread2 run thread3 run thread2 singleton = com.buckyball.dp.singleton.Singleton@fa0094 thread3 singleton = com.buckyball.dp.singleton.Singleton@fa0094 thread1 singleton = com.buckyball.dp.singleton.Singleton@fa0094
如何解决?
1、对 getInstance 方法加 synchronized 关键字修饰
/** * @Title Singleton.java * @Package com.buckyball.dp.singleton * @author Roy * @email 574613441@qq.com * @weixin qqqq123456 * @date 2018年6月28日 下午8:05:57 * @Desc */ package com.buckyball.dp.singleton; /** * @Title Singleton.java * @Package com.buckyball.dp.singleton * @author Roy * @email 574613441@qq.com * @weixin qqqq123456 * @date 2018年6月28日 下午8:05:57 * @Desc */ public class Singleton1 { private Singleton1(){} private static Singleton1 singleton = null; public static synchronized Singleton1 getInstance(){ if(singleton == null){ singleton = new Singleton1(); } return singleton; } }
无论运行多少次,妥妥的每次都是一样的
thread1 run thread3 run thread2 run thread1 singleton = com.buckyball.dp.singleton.Singleton1@ba8af9 thread2 singleton = com.buckyball.dp.singleton.Singleton1@ba8af9 thread3 singleton = com.buckyball.dp.singleton.Singleton1@ba8af9
这样写的问题:
synchronized关键字锁住的是这个对象,这样的用法,在性能上会有所下降,因为每次调用getInstance(),都要对对象上锁,事实上,只有在第一次创建对象的时候需要加锁,之后就不需要了,所以,这个地方需要改进:。
于是看到有人推荐这么写:妥妥的告诉你,给你个大大的叉。synchronized不能锁null对象。
public static Singleton2 getInstance() { if (singleton == null) { synchronized (singleton) { //这里会报空指针java.lang.NullPointerException if (singleton == null) { singleton = new Singleton2(); } } } return singleton; }
2、直接声明属性的时候就创建对象
public class Singleton3 {
private Singleton3(){}private static Singleton3 singleton = new Singleton3();public static Singleton3 getInstance(){return singleton;}}
输出如下:
thread1 run thread2 run thread3 run thread1 singleton = com.buckyball.dp.singleton.Singleton3@46b10e8e thread3 singleton = com.buckyball.dp.singleton.Singleton3@46b10e8e thread2 singleton = com.buckyball.dp.singleton.Singleton3@46b10e8e
喜欢我的文章,请微信搜索公众账号【IT殿堂】或长按扫码关注,会定期更新原创、系列文章。
关注后回复【java】、【android】、【c++】等关键词有惊喜哦。
也可以加我微信:qqqq123456
长按扫码关注:
本系列其它文章:
以上是关于五分钟精通设计模式--单例模式的主要内容,如果未能解决你的问题,请参考以下文章