目录
单例模式
饿汉模式
懒汉模式
单例模式
所谓单例模式,就是在有些场景中,有些特定的类,只能创建一个实例(对象),当程序员不小心创建多个实例,就会出现编译报错.
★ 这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
★单例模式是一种创建型设计模式,它确保一个类只有一个实例,并提供了一个全局访问点来访问该实例。
注意:
- 1、单例类只能有一个实例。
- 2、单例类必须自己创建自己的唯一实例。
- 3、单例类必须给所有其他对象提供这一实例。
优点:
- 1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。
- 2、避免对资源的多重占用(比如写文件操作)。
缺点:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
使用场景:
- 1、要求生产唯一序列号。
- 2、WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。
- 3、创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。
注意事项:getInstance() 方法中需要使用同步锁 synchronized (Singleton.class) 防止多线程同时进入造成 instance 被多次实例化。
单例模式又分为饿汉模式和懒汉模式
饿汉模式
所谓饿汉模式,就是在类加载阶段,就把实例创建出来,这种效果就给人一种特别急切的感觉,就称为单例模式.
实现方式
这个代码通过两个关键操作 保证实例是唯一的
★static这个操作,是让当前的instance 属性是类属性,类属性是长在类对象上的,类对象有事唯一实例,所以保证了这个实例是唯一的
★将构造方法设为private,外面的代码是无法通过new在来创建这个实例的
在多线程操作中,饿汉模式只有一个retutn操作,这只是一个"读操作",是线程安全的
懒汉模式
这个实例并非在类加载的时候创建的,而是在第一次真正使用的时候,才去创建,避免内存的浪费.
在多线程操作中,懒汉模式既有读操作也有写操作 ,下面画图演示
在上面的操作中,多个线程读到的instance的值都是null,就会多次触发new 操作,此时就是线程不安全的.上面的线程安全问题,本质上是,读和写的操作不是原子的,导致t2线程读到的值是t1还没来得及修改的,可以通过加锁解决线程安全问题.
上面代码导致每次getInstance的时候都需要加锁,但加锁是有开销的,所以只需要在new 对象之前加锁是有必要的, 一旦new 对象之后,调用getInstance的值都是非空的,就会直接触发return 操作,相当于一个比较操作,一个返回操作,都是读操作,此时不加锁也没事,对上面代码在进行改进,当instance为空的时候在进行加锁
在上面的代码中还会出现内存可见性问题和指令重排序问题,在这里就要先理解一下Instance = new SingletonLazy这个操作了,这个操作分为三步
1. 申请内存空间
2. 调用构造方法,把这个内存空间初始化成一个合理的值
3. 把内存空间的值赋给Instance引用.
此时编译器为了提高运行效率,可能会进行指令重排序,调整代码执行顺序,导致多线程操作出现问题.
至此一个完整版的懒汉模式就完成了