单例模式学习笔记(详细)

单例模式

一、什么是单例模式

单例模式是一种常见的设计模式,定义是这个类只允许有一个实例对象存在

二、使用场景

spring的依赖注入,购买东西时的购物车,window系统的回收站等等

三、实现方式

1. 懒汉式

   public class Lazy {
		private static Lazy instance;
		private Lazy(){}
		public static Lazy getInstance(){
			if(instance == null){
				instance = new Lazy();
			}
			return instance;
		}
	}

懒汉式,实行延迟加载,不会初始化,在需要用到实例的时候才去创建,用到的时候先检查实例存不存在,存在就返回,不存在创建一个返回,可以再单线程下使用,多线程下是不安全的,一个线程通过了判空判断的同时,另一个线程也通过了判空判断,就会同时创建多个实例,想要线程安全可以加synchronized关键字,但是会影响效率,不推荐。

  • 优点:延迟加载,不会浪费内存
  • 缺点:线程不安全,加synchronized会影响效率

2. 饿汉式

  public class Hungry {
		private static Hungry instance = new Hungry();
		
		private Hungry(){}
		
		public static Hungry getInstance(){
			return instance;
		}
	}

饿汉式,在初始化的时候创建实例,不会存在线程安全问题

  • 优点:线程安全
  • 缺点:没有延迟加载效果,如果没有用到这个实例就会浪费内存

3. 双检锁

	public class DoubleCheck{
		private volatile static DoubleCheck instance;
		
		private DoubleCheck(){}
		
		public static DoubleCheck getInstance(){
			if(instance == null){
				synchronized(DoubleCheck.class){
					if(instance == null){
						instance = new DoubleCheck();
					}
				}
			}
			return instance;
		}		
	}

双检锁,外面一层if判断对象存在就不会执行加锁代码,提高了效率,synchronized加上里面一层 if 保证了线程安全

  • 优点: 线程安全,延迟加载,效率高,推荐

为什么加volatile关键字

原因是 instance = new DoubleCheck(); 这句代码不是原子性的。

创建一个对象分为三步:

  1. 分配 instance 对象内存空间 N
  2. 在内存 N 初始化对象
  3. 把 N 的地址赋值给对象 instance

这时在实例instance指向N的时候,instance是不为空的

但是,编译时编译器可能会将2,3顺序重新排序,造成顺序为1-3-2

  1. 分配 instance 对象内存空间 N
  2. 把 N 的地址赋值给对象 instance
  3. 在内存 N 初始化对象

线程A,在内存 N 初始化对象之前就将 N 的地址赋值给了instance
这时线程B调用了getInstance方法,发现instance不为null
这个时候instance没有初始化对象,线程B会将这个未初始化的对象返回,线程B在使用instance对象时就会出现问题

使用volatile修饰instance可以防止指令重排序,就可以避免这种现象发生。

4. 静态内部类

	public static class Single{
		private static class Singleton{
			private static final Singleton INSTANCE = new Singleton();
		}
		
		private Singleton(){}
		
		public static final Singleton getInstance(){
			return Singleton.INSTANCE;
		}
	}

静态内部类,在第一次使用时才会初始化内部类Singleton,创建实例,保证了只有一个实例,并实现了延迟加载,加上静态域是线程安全的,减少了synchronized的开销。

  • 优点:线程安全,延迟加载,没有锁开销,效率高,推荐。

四、总结

这四种是比较常见的单例模式实现方式

  • 懒汉式实现了延迟加载,效率较高,但是线程不安全,适合单线程的时候使用
  • 饿汉式在初始化的时候创建对象,保证了线程安全,但是在没有使用到这个对象的时候就浪费了内存空间,可以在多线程使用
  • 双检索整合了懒汉式和饿汉式的优点,是线程安全,又实现了延迟加载,可以在多线程使用
  • 静态内部类方式也是拥有懒汉式和饿汉式的优点,线程安全,延迟加载,还减少了锁的开销,提高了效率,推荐使用这种方式
  • 8
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值