五分钟精通设计模式--单例模式

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

长按扫码关注:


本系列其它文章:




以上是关于五分钟精通设计模式--单例模式的主要内容,如果未能解决你的问题,请参考以下文章

JAVA OOP 编程-常用设计模式

分分钟带你理解单例

设计模式篇:单例模式

5分钟用Python创建一个单例模式

如何实现单例模式

设计模式-单例模式