单例设计模式

单例模式可以保证系统中,应用该模式的类只有一个对象实例

  • 使用场景

    • spirng的ioc中的对象
    • springboot中的controller,service,dao层中通过@Autowried的依赖注入对象默认都是单例的
  • 分类

    • 懒汉:延迟创建对象
    • 饿汉:提前创建对象
  • 实现步骤

    • 1、私有化构造函数
    • 2、提供获取单例的方法,统一返回对象
  • 接下来就直接进入代码啦,先演示的是懒汉模式

    • 先创建一个类SingletonLazy.class

public class SingletonLazy{
// 实例属性
private static SingletonLazy instance;
	
//私有化实例方法
private SingletonLazy(){};

//提供方法返回对象
public static SingletonLazy getInstance1(){
	if(instance == null){
		instance =  new SingletonLazy();
	}
	return instance;
}

}

  • 上面这种方法在多线程的情况是不安全的,所以我们应该改为线程安全的
public static synchronized SingletonLazy getInstance2(){
	if(instance == null){
		instance =  new SingletonLazy();
	}
	return instance;
}
  • 上面这种方法虽然保证了线程安全,但是在大量线程并发的情况,性能会比较差,所以我们可以尝试先缩小锁的范围。
public static SingletonLazy getInstance3(){
	if(instance == null){
		//锁住当前对象
		synchronized(SingletonLazy.class){
			instance =  new SingletonLazy();
		}
	}
	return instance;
}
  • 但是你以为上面的就是没有问题了吗,其实你仔细分析的话,会发现上面虽然缩小了锁的范围,但是并没有能完全保证线程安全,在极端的情况下,假设abc三个线程同时执行完了 if(isntance == null){ 这串代码,那么接下来就会发生new了三次对象了。所以我们可以加入双重检查锁定。
public static SingletonLazy getInstance4(){
	if(instance == null){
		//锁住当前对象
		synchronized(SingletonLazy.class){
			if(instance == null){
				instance = new SingletonLazy();
			}
		}
	}
	return instance;
}
  • 到这里,其实看起来就已经很完美了是不?但是不要以为稳了,这里面还是并非安全的,我们知道java中实例化一个对象要经过大概三个步骤
    • 1、申请内存空间
    • 2、在空间内创建对象
    • 3、将对象地址赋值给引用 instance
    • 这里面设计到一个问题--JVM指令重排,不懂的可以先百度,2跟3步骤不是保证顺序的,也就是说可能执行顺序为 1>3>2,这个时候我们的代码就可能会出现new出来的对象是会有问题的(有引用了,但是对象还没真正在空间中创建完成),也就是说其他线程可能用了非完整的对象,这样可能会引发大问题。所以我们这里可以加入volatile关键字,可以禁止指令重排

public static volatile SingletonLazy instance;
public static SingletonLazy getInstance5(){
	if(instance == null){
		synchronized(SingletonLazy.class){
			if(instance == null){
				instance = new SingletonLazy();
			}
		}
	}
	return instance;
}
  • 饿汉模式
  • 创建一个SingletonHungry.class
public class SingletonHungry{
	//饿汉模式当然是直接实例化对象啦
	private static SingletonHungry instance = new SingletonHungry();

	//私有化构造方法
	private SingletonHungry(){};

	//返回对象的方法
	public static getInstance(){
		return instance;
	}

}
  • jdk中有哪些用到了单例模式
  • Runtime类
    image.png

总结:

  • 实战中如何选择
  • 饿汉实现非常简单,也没有安全问题,但是类加载的时候就创建对象,如果是对象非常大和耗时复杂的,就不建议这种方式。其他情况建议采用懒汉模式

评论

何首污 : 叼毛
渣男 : 456
三群的弟弟 : 123
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×