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对象进行使用就可能发生错误。
其他例子:参考自《大话设计模式》有些类也需要计划生育。
注:以上文章仅是个人总结,若有不当之处,望不吝赐教