单例模式是什么?如何实现它?

news/2024/6/3 20:07:46 标签: java, 单例模式

1. 单例模式基本概念及分类

       ● 基本概念:软件开发的一种设计模式(不止一种).单例代表单个实例,也就是说通过Java语法让一个类只能创建一个实例(对象).

       ● 分类:根据单例模式可再细分为"饿汉"和"懒汉"模式.


2. 单例模式的实现

2.1 "饿汉"模式 实现

          ● 简单解释:饿代表急迫,单例类中不管需不需要这个唯一对象都默认实例化它.例如,在疫情期间大家都不管价钱和自己当前的实际需求(吃一顿买一个月的量),所有吃的基本统统拿下.秉持着把超市抢购一空的原则,我成功得啥也没买着,被我妈骂了一顿.那我们如何用Java代码实现呢?

          ● 实现思路:

             ① 首先,新建一个类让该类只能实例化一个对象,且让这个对象原地就直接实例化.根据Java知识,我们可以知道在Jvm中,类对象唯一,那么其中的成员也唯一,所以我们将这个对象用static关键字修饰,让这个对象变成类对象.为了防止外部类随意访问,我们把它的访问权限设为私有(private关键字修饰).让外部通过只能通过get方法读取.

             ② 然后,让单例类的构造方法变成私有,这样外部类就无法重新实例化该单例类对象.

//饿汉模式
class Singleton1 {
    private static Singleton1 instance = new Singleton1();

    private Singleton1() {

    }

    public static Singleton1 getInstance() {
        return instance;
    }

}

注:通过代码我们可以知道饿汉模式的线程是安全的,因为它只涉及到读操作(多个线程读取同一个变量线程是安全的).

2.2 "懒汉" 模式实现

           ● 简单解释: 懒就是不急,单例类中只有唯一对象为空的时候才去实例化它.例如,除非家里没吃的再去买能够做一顿的菜.

           ● 实现思路:

              ①,②步和饿汉模式一样,但是对象就不是原地直接初始化了.而是在get方法中判断该对象是否为空,为空我们再去实例化,否则直接返回该对象.但是如果我们直接这样实例化单例类的对象可以吗?

public static Singleton2 getInstance() {
        if (instance == null) {
            instance = new Singleton2();
        }
        return instance;
}

在这之前我分享过线程不安全的原因及解决为什么会出现线程安全问题?如何解决?-_Crystal_bit的博客-CSDN博客

我们可以知道判断instance对象是否为空和初始化instance对象可能会造成修改操作不是原子的线程安全问题.多个线程同时启动可能会造成实例化多个单例类的对象的问题.所以我们需要让if和new这两个操作变成原子的,给这两个操作都加锁.

    public static Singleton2 getInstance() {
        //让if判断和实例化instance对象操作变成原子的
        synchronized (Singleton2.class) {
            if (instance == null) {
                instance = new Singleton2();
            }
        }
        return instance;
    }

在这基础之上,多个线程调用getInstance()方法就不会实例化出多个单例类的对象了,但是因为加了锁,那么多个线程加锁就会导致锁竞争,所以为了减少不必要的加锁操作,我们就先判断instance对象是否为空,为空了我们再去加锁,如果不为空,我们就直接返回instance对象.

    public static Singleton2 getInstance() {
        //减少不必要的加锁操作
        if (instance == null) {
            //让if判断和实例化instance对象操作变成原子的
            synchronized (Singleton2.class) {
                if (instance == null) {
                    instance = new Singleton2();
                }
            }
        }
        return instance;
    }

嘿嘿~大家觉得没有任何问题了吗?并没有,new操作还可能发生指令重排序线程不安全问题,为什么呢? new操作主要分为三步:

1) 创建出对象(建好房子)

2) 构造对象(装修房子)

3) 将生成的地址赋值给对象引用(拿到钥匙).

在多线程情况下,线程调度无序,那么某个线程可能会拿到一个没有构造好的对象(啥也没有,属性都是默认的),那么我们去使用该对象成员变量或方法时,可能就会发生一系列的错误.所以为了保证线程拿到完整的对象,我们需要用volatile关键字修饰单例类的唯一对象.完整代码如下:

class Singleton2 {
    //用volatile关键字修饰解决指令重排序问题
    volatile private static Singleton2 instance;

    private Singleton2() {

    }

    public static Singleton2 getInstance() {
        //减少不必要的加锁操作
        if (instance == null) {
            //让if判断和实例化instance对象操作变成原子的
            synchronized (Singleton2.class) {
                if (instance == null) {
                    instance = new Singleton2();
                }
            }
        }
        return instance;
    }
}

注:是否存在内存可见性的线程不安全问题仍有疑问.


这样单例模式的实现就分享完毕啦~


http://www.niftyadmin.cn/n/177015.html

相关文章

Centos7 挂载 ISO镜像

切到mnt目录:cd /mnt mkdir iso确保centos镜像在服务上存在,磁盘挂载mount -o loop /home/xx.iso /mnt/iso查看是否挂载成功df -h出现红色的部分表示挂载成功修改源切目录并修改yum源:cd /etc/yum.repos.dllvim Centos-Base.repo修改后yum clean allyum list安装lrz…

UWB芯片DW3000介绍二数据包接收流程

PHY接收一个包的接收是由一个主控制器MCU请求启用接收器。这可以在设备处于IDLE_RC或IDLE_PLL状态时完成。如果设备处于IDLE_RC状态,它将首先校准,使能PLL并切换到IDLE_PLL状态,然后进入RX状态。但是,在设备上电后的第一次RX启用之…

进程相关命令

1.查看进程 ps aux / ajxa:显示终端上的所有进程,包括其他用户的进程u:显示进程的详细信息x:显示没有控制终端的进程j:列出与作业控制相关的信息 2.STAT参数意义: D 不可中断 Uninterruptible&#xff08…

零售数据总结经验:找好关键分析指标和维度

各位数据的朋友,大家好,我是老周道数据,和你一起,用常人思维数据分析,通过数据讲故事。 每逢月末、季末、年终,运营部门的同事又要开始进行年终总结分析。那么,对零售连锁企业来说,…

腾讯后端开发实习一面(24届)

毫无准备的腾讯一面,最近都在忙比赛去了,突然收到腾讯一面的邮件,直接没准备。。。 总结,除了Vue其他的都挺好,但是腾讯hr为啥Vue面我四个问题,不是面的后端开发吗,好难呀,都只能随…

全网最完整,接口测试总结彻底打通接口自动化大门,看这篇就够了......

目录:导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜)前言 所谓接口&#xff0…

CF1740F Conditional Mix

CF1740F Conditional Mix 题目大意 有一个正整数nnn和一个长度为nnn的序列aaa,1≤ai≤n1\leq a_i\leq n1≤ai​≤n。 把每个aia_iai​看成一个一元集{ai}\{a_i\}{ai​},每次可以合并两个交集为空的集合。可以经过任意次合并。设合并完每个集合的元素个…

蓝桥杯-本质上升序列(动态规划问题)

本质上升序列-动态规划问题1、题目描述2、解题思路3、代码实现参考1、题目描述 本题为填空题,只需要算出结果后,在代码中使用输出语句将所填结果输出即可。 小蓝特别喜欢单调递增的事物。 在一个字符串中,如果取出若干个字符,将这…