文章目录
✍一、什么是设计模式?
模式:在某些场景下,针对某类问题的某种通用的解决方案。
场景:项目所在的环境
问题:约束条件,项目目标等
解决方案:通用、可复用的设计,解决约束达到目标。
用生活中的事务来介绍:
设计模式好⽐象棋中的 “棋谱”. 红⽅当头炮, ⿊⽅⻢来跳. 针对红⽅的⼀些⾛法, ⿊⽅应招的时候有⼀些固定的套路. 按照套路来⾛局势就不会吃亏.
✍二、单例模式是什么?
单例模式是指在内存中只会创建且仅创建一次对象的设计模式。在程序中多次使用同一个对象且作用相同时,为了防止频繁地创建对象使得内存飙升,单例模式可以让程序仅在内存中创建一个对象,让所有需要调用的地方都共享这一单例对象。
简单概括:
- 单例模式能保证某个类在程序中只存在唯⼀⼀份实例,而不会创建出多个实例.
✍三、单例模式的类型
单例模式具体的实现⽅式有很多. 最常⻅的是 “饿汉” 和 “懒汉” 两种.
- 饿汉式:在类加载过程中就创建了实例。
- 懒汉式:在真正需要使用时,才会创建实例。
1.饿汉式
class Singleton{
private static Singleton instance = new Singleton();
public static Singleton getInstance(){
return instance;
}
private Singleton(){}
}
类在加载时,就会创建一个实例。在调用时,之间返回这一实例就好。
可以简单的认为,在程序启动时就创建了实例。
2.懒汉式
class Singletonlazy{
public static Singletonlazy instance = null;
public Singletonlazy getInstance(){
if ( instance == null){
instance = new Singletonlazy();
}
return instance;
}
private Singletonlazy(){}
}
这是一段存在些许问题的代码,不过可以直观的感受到两者之间的区别。
在接下来,会对如上懒汉式代码进行优化。
3.优化懒汉式
我们先将懒汉式代码放置如下
class Singletonlazy{
public static Singletonlazy instance = null;
public Singletonlazy getInstance(){
if ( instance == null){
instance = new Singletonlazy();
}
return instance;
}
private Singletonlazy(){}
}
在如上懒汉式的代码中,如果在多线程情况下,就会出现一些问题
在多线程中,线程是抢占式执行的。
那么就会给程序带来一些问题
由于线程的抢占式执行,虽说不会造成空间的浪费
但是时间的消耗确实客观存在的。
那么解决这个问题,就进行加锁操作。
public Singletonlazy getInstance(){
synchronized (lock){
if ( instance == null){
instance = new Singletonlazy();
}
}
return instance;
}
这样加锁,就是将 if 和 new 打包成一个原子操作
但是这样也会出现问题
那么如何解决这个问题呢?
我们在锁的外层,在添加一个判断条件
public Singletonlazy getInstance(){
if (instance == null) {
synchronized (lock){
if ( instance == null){
instance = new Singletonlazy();
}
}
}
return instance;
}
注意:
这里的两个if条件虽然内容一样,但是意义却完全不同
- 第一个if是判断是否要进行加锁操作
- 第二个if是判断是否要实例创建对象
如上代码已经解决了多线程情况下的线程安全问题。
也解决了执行效率的问题。
但是还存在一个问题
指令重排
4.指令重排
概念:
通俗的说,就是在不改变代码逻辑的条件下,通过更改指令的执行顺序,来达到优化代码的效果。
举例:
在这一行代码中,一个创建对象实例的过程可以在指令的角度分为三步
- 申请内容空间
- 调用构造方法(对内存空间进行初始化)
- 把此时内存空间的地址,赋值给instance引用
在指令重排的优化下
可能有
1 --》3 --》 2
1 --》2 --》 3
这样两种情况
1是一定在第一步的,因为是要在保证代码逻辑的前提下,才能进行指令重排。
那么如何解决呢?
引入volatile
public static volatile Singletonlazy instance = null;
使用volatile关键字修饰的变量,可以保证其指令执行的顺序与程序指明的顺序一致,不会发生顺序变换
5.完整代码
class Singletonlazy{
public static volatile Singletonlazy instance = null;
public static Object lock = new Object();
public Singletonlazy getInstance(){
if (instance == null) {
synchronized (lock){
if ( instance == null){
instance = new Singletonlazy();
}
}
}
return instance;
}
private Singletonlazy(){}
}
以上就是本文所有内容,如果对你有帮助的话,点赞收藏支持一下吧!💞💞💞