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

本文由【程序员伟哥】原创,转载请注明出处,不注明请默念三遍【我爱技术】后转载!
更多精彩请关注微信公众号:【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@13c339f
thread2 singleton = com.buckyball.dp.singleton.Singleton@ba8af9
thread3 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

源码地址:https://github.com/tianshanaoxue/design-pattern.git

喜欢我的文章,请微信搜索公众账号:【IT殿堂】,会定期更新原创、系列文章

关注后回复【java】、【Android】、【c++】等关键词有惊喜哦。
也可以加我微信:qqqq123456



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值