《Design Patterns》Singleton.积跬步系列

Singleton:单例模式

先代码

package h.l.demo.singleton;

/**
 * 
 * @author: Is-Me-Hl
 * @date: 2020年3月7日
 * @Description: 单例模式测试类
 */
public class Singleton {

	public static void main(String[] args) {
		Instance instance = Instance.getInstance();
		Instance instance2 = Instance.getInstance();
		System.out.println("instance == instance2 result :" + (instance == instance2));

		Instance3 instance3 = Instance3.getInstance3();
		Instance3 instance4 = Instance3.getInstance3();
		System.out.println("instance3 == instance4 result :" + (instance3 == instance4));
	}
}

/**
 * 
 * @author: Is-Me-Hl
 * @date: 2020年3月3日
 * @Description: 单例模式:重要一点是构造函数私有,即无法通过new的方式创建对象实例,对外提供唯一的类方法供调用者访问
 * 
 *               懒汉式,指的是当需要获取该实例时才去判断实例是否存在,不存在再创建。
 *               饿汉式:指的是一开始我就给实例化好,需要的时候我就直接给你。
 * 
 *               两者比较各有优缺点:饿汉式一开始就实例,占内存,不如懒汉式中等需要时再创建对象的好。但懒汉式也存在问题,获取实例对象的时候
 *               如果在多线程环境中
 *               ,可能存在线程安全,准确说可能多个线程同时都去获取了实例,发现没有该实例,又同时去创建了一个实例,这样就不是单例
 *               的了,需要使用双层锁定的方式才能避免,这相对于饿汉式多了这些步骤。
 */
/**
 * 
 * @author: Is-Me-Hl
 * @date: 2020年3月4日
 * @Description: 单例模式-懒汉式-单线程环境
 */
class Instance {
	private static Instance instance;

	// 将构造函数私有,不允许通过new的方式创建对象
	private Instance() {
	}

	public static Instance getInstance() {
		if (instance == null) {
			instance = new Instance();
		}
		return instance;
	}
}

/**
 * 
 * @author: Is-Me-Hl
 * @date: 2020年3月4日
 * @Description: 单例模式-懒汉式-多线程环境-双重锁定-volatile防止指令重排
 */
class Instance2 {
	private volatile static Instance2 instance2;

	// 创建一个锁对象
	private static Object obj = new Object();

	// 将构造函数私有,不允许通过new的方式创建对象
	private Instance2() {
	}

	public static Instance2 getInstance2() {
		if (instance2 == null) {
			synchronized (obj) {
				if (instance2 == null) {
					instance2 = new Instance2();
				}
			}

		}
		return instance2;
	}
}

/**
 * 
 * @author: Is-Me-Hl
 * @date: 2020年3月4日
 * @Description: 单例模式-饿汉式
 */
class Instance3 {
	private static Instance3 instance3 = new Instance3();

	// 将构造函数私有,不允许通过new的方式创建对象
	private Instance3() {
	}

	public static Instance3 getInstance3() {
		return instance3;
	}
}

测试结果:
在这里插入图片描述

后分析

  • 个人建议:写代码是件幸福的事,So,do it

单例模式,定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点。实现方式:最好的办法就是让类自身负责保存它的唯一实例,这个类可以保证没有其他实例可以创建,并且它可以提供一个访问该实例的方法。
什么时候使用单例模式?当类只允许创建一个实例的时候,即可使用该模式。对于上述demo,单例模式分单线程和多线程的区别,单线程中,无需使用锁,好比一顿大餐就一个人吃,那需要那么多的繁文缛节,排队进餐等。多线程环境就需要上锁,放置多个线程同时发现实例为null,去调用实例化方法去同时创建新的实例。使用了volatile是保证实例化过程顺序的一致,提供线程安全,防止当线程抢到锁,刚给该实例分配内存还没初始化时,其他线程发现该实例不为null了,就拿该实例对象去执行操作,从而产生错误。

看似简单的一段赋值语句:instance = new Singleton(); 其实JVM内部已经转换为多条指令:
memory = allocate(); //1:分配对象的内存空间
ctorInstance(memory); //2:初始化对象
instance = memory; //3:设置instance指向刚分配的内存地址
但是经过重排序后如下:
memory = allocate(); //1:分配对象的内存空间
instance = memory; //3:设置instance指向刚分配的内存地址,此时对象还没被初始化
ctorInstance(memory); //2:初始化对象
可以看到指令重排之后,instance指向分配好的内存放在了前面,而这段内存的初始化被排在了后面,在线程A初始化完成这段内存之前,线程B虽然进不去同步代码块,但是在同步代码块之前的判断就会发现instance不为空,此时线程B获得instance对象进行使用就可能发生错误。

其他例子:参考自《大话设计模式》有些类也需要计划生育。


注:以上文章仅是个人总结,若有不当之处,望不吝赐教

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值