多线程|饿汉模式和懒汉模式
单例模式是只有单个实例的模式,应用在只能有一个实例的场景中。单例模式有很多种,这里介绍饿汉模式和懒汉模式两个单例。
一、饿汉模式
“饿汉”是一种形象的描述,“饿汉”看到吃的就非常急切,把这种急切的形象类比到Java中就是在类加载阶段就把实例创建出来了。什么是类加载?Java代码中的每个类,都会在编译完成之后得到.class文件,JVM中就会加载这个.class文件,读取其中的二进制指令,并且构造出对应的对象,这就是类加载。
实现饿汉模式:
static是实现饿汉模式的核心,static可以用来修饰成员变量和方法和成员方法,被修饰的成员变量和成员方法不依赖实例去访问,只要类被加载,通过类名就可以直接访问。由于被static修饰的成员变量和成员方法不依赖实例去访问,那么就不会因为实例的多次创建而产生多份数据,被static修饰的变量或成员在内存中只存在一份。
代码实现:
class Singleton{ private static Singleton instance = new Singleton(); public static Singleton getInstance(){ return instance; } private Singleton(){ } }
分析上述代码实例的唯一性:instance被static修饰,让instance具有类属性,在类加载之后instance在内存中只存在一份,同样的static修饰getInstance()方法,每次通过类名访问这个方法时,访问的都是同一份数据,通过这个方法得到的instance也是同一份数据,这样保证了实例的唯一性,同时,构造方法使用private修饰,保证在该类之外的代码中都不能创造新实例。
二、懒汉模式
与“饿汉模式”一样,“懒汉”也是形象的描述,与“饿汉”在类加载就创建实例不同,“懒汉”是在第一次需要的时候才创建实例。
代码是实现:
class SingletonLazy{ private static SingletonLazy instance = null; public static SingletonLazy getInstance(){ if(instance == null){ instance = new SingletonLazy(); } return instance; } private SingletonLazy(){ } }
懒汉模式保持实例唯一性与饿汉模式相同。
我们上述讨论的只是饿汉模式与懒汉模式怎么创建,那么多线程环境下,这两个线程是否具有线程安全问题呢?
先来看看饿汉模式
饿汉模式在类加载阶段就创建好实例,多线程环境下,只涉及读操作,因此,饿汉模式多线程环境下是安全的。
懒汉模式创建实例是在第一次使用时才创建,涉及到读和写,因此懒汉模式多线程下是不安全的。
以上就是线程不安全的一种情况。那么如何将懒汉模式变成线程安全的呢?首先第一步是加锁,保证创建实例的过程是具有原子性的,进行如下操作:
上述的加锁操作是让写和读具有原子性,此时多个线程要进行读和写的操作就必须等到锁被释放。上面说过,懒汉模式是第一次使用的时候需要创建实例,后面的使用就不要创建实例了,但是上述代码每次获取instance都要进行加锁操作,而加锁操作是非常消耗资源的,其实只需要在第一次创建出对象之前加锁,其他时候是没有必要的,因此,可以再给代码加上一个判定,判定instance是否为空,为空加锁创建,不为空直接返回instance。
代码写到这,其实还存在一个问题没有考虑,那就是内存可见性问题,由于JVM的优化,只有第一次是将内存中的值读到寄存器中,而且编译器还会进行重排序。
new操作分以下三步:
1、申请内存空间
2、调用构造方法,把内存空间初始化成一个合理的对象
3、把内存空间的地址给instance使用
正常情况下,是按照123的顺序来执行的,但是编译器会进行指令重排序来提高效率,所以new操作的顺序会被打乱,此时也会存在线程安全问题,解决办法是加上volatile关键字。
最终代码:
class SingletonLazy{ private volatile static SingletonLazy instance = null; public static SingletonLazy getInstance(){ if(instance == null){ synchronized (SingletonLazy.class){ if(instance == null){ instance = new SingletonLazy(); } } } return instance; } private SingletonLazy(){ } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)