单例模式(饿汉/懒汉)

单例模式: 饿汉模式/懒汉模式
        单例模式是一种常见的设计模式,类似于棋谱,把一些对局中常见场景 ,整理总结,让别人来研究学习。
设计模式有多种,以后有些工作经验再学习 ,有了一定的经验才能明白其中的道理
为什么会有设计模式
        代码领域里程序猿水平参差不齐, 于是就有大佬们,根据一些常见的需求场景, 整理出来了一些应对的解决办法, 这个东西就是"设计模式"。 能够提高程序猿的“下限”按照设计模式来写代码,不说代码能写多好,但是不会很差。
饿汉模式:
类加载的同时, 创建实例。
懒汉模式:
效率更高
类加载的时候不创建实例. 第一次使用的时候才创建实例
        如果整个代码后续没人调用getInstance, 与 饿汉模式 相比,这样就把构造实例的过程省去了, 或者即使有代码后续调用getInstance,但是调用的时机比较晚,这个时候创建实例的时机也就较晚,就 和其他的耗时操作错开了,使系统执行更加高效。(一般程序刚启动的时候,要初始化的东西很多,系统资源紧张)
饿汉模式线程安全,懒汉不安全
饿汉模式:线程安全
这个操作只是单纯的“读数据" ,不涉及到修改。
懒汉模式:线程不安全
此时懒汉模式会创建多个实例不符合单例模式要求出现BUG
如何解决懒汉模式线程不安全问题?
(1)加锁:synchronized,保证原子性
(2)关键字:Volatile,内存可见性,禁止指令重排序
 加锁
        通过 synchronized 加锁方式,线程安全问题解决了。 但是又引入了新的问题, 线程不安全,也不是一直都不安全。只是实例创建之前(首轮调用的时候) 才会触发线程不安全问题,在第一次 实例创建好之后,instance不为null也就不涉及修改操作,此时线程也就安全了。
        由于 , 加锁开销其实较大 ,加锁可能就会涉及到用户态->内核态之间的切换,这样的切换成本是比较高的。因此 加锁绝对不是“无脑加”,在我们 只在需要时加锁。
加锁代价比较大,因此该加锁枷锁,不该加就去掉。
只需要在加锁前面加一个判断,判断是否需要加锁
Volatile关键字:
指令重排序(编译器进行代码优化操作):造成线程不安全
new操作本质上也分成三个步骤:
1.申请内存,得到内存首地址。
2.调用构造方法来初始化实例。
3.把内存的首地址赋值给instance引用。
        上述步骤可能编译器会进行“指令重排序”的优化, 在单线程的角度下, 2和3是可以调换顺序的。 (单线程的情况下,此时2和3先执行谁,后执行谁效果样)
设想:
        假设此处触发指令重排序, 并且按照1  3  2的顺序执行的。 有可能t1执行了1和3之后,instance得到了不完全的对象,只是有内存,但是内存上的数据是无效的。接下来t2线程执行调用getInstance方法,判断instance不为null,直接返回instance出现了BUG,造成线程不安全。
因此就要用Volatile关键字禁止指令重排序
完整单例模式的懒汉实现:
        另一方面,再想想, 如果线程更多的话, 有的线程在读Instance,有的线程在修改, 也存在内存可见性问题, 加上volatile也是应当。
简单完整单例模式代码如下:
class SingletonLazy{
   private volatile static SingletonLazy instance = null;
    //类加载的时候不创建实例. 第一次使用的时候才创建实例
    public static SingletonLazy getInstance(){
        if(instance == null){//判断是否加锁
            //给SingletonLazy单例类加锁
            synchronized (SingletonLazy.class){
                if(instance == null){//判断是否创建实例
                    instance = new SingletonLazy();
                }

            }
        }
        return instance;
    }
    //将此类构造方法设置为private,此时就无法在此类外创建实例对象
    private SingletonLazy(){  }
}


public class Singleton {
    public static void main(String[] args) {
        SingletonLazy instance = SingletonLazy.getInstance();
    }
}

Java单例模式包括饿汉式和懒汉式两种实现方式。饿汉式是在类加载阶段就创建实例并持有,而懒汉式则是在需要时才创建实例。 饿汉模式是指在类加载阶段就创建出实例的,因此它的实例化过程相对于普通情况要早很多。这也是为什么叫“饿汉”的原因,就像一个饥饿的人对食物没有抵抗力,一下子就开始吃了一样。 懒汉模式是指在需要时才创建实例。这种方式的优点是节省了资源,只有在需要时才会进行实例化。但是它的缺点是在多线程环境下可能会导致多个线程同时创建实例的问题,需要进行额外的线程安全措施来解决这个问题。 总结来说,饿汉式适合在应用启动时就需要创建实例的情况,因为它的实例化过程早于普通情况。而懒汉式适合在需要时才创建实例的情况,可以节省资源。 需要注意的是,单例模式的使用要根据具体的适应场景来决定,不同的情况下选择不同的实现方式。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Java设计模式之单例模式——饿汉式、懒汉式(初了解)](https://blog.csdn.net/m0_68062837/article/details/127307310)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [Java多线程案例之单例模式饿汉懒汉)](https://blog.csdn.net/qq_63218110/article/details/128738155)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值