Java之单例模式

news2024/7/6 18:36:19

目录

一.上节内容

1.什么是线程安全

2.线程不安全的原因

3.JMM(Java内存模型)

4.synchronized锁

5.锁对象

6.volatile关键字

7.wait()和notify()

8.Java中线程安全的类

二.单例模式

1.什么是单例

2.怎么设计一个单例

1.口头约定

2.使用编程语言的特性

三.饿汉模式

四.懒汉模式

1.单线程下的懒汉模式

2.多线程下的懒汉模式

3.分析线程不安全的原因

4.加锁解决这个问题

5.双重检查锁

6.加volatile优化(单例懒汉模式最终版)


一.上节内容

上节内容指路:Java之线程安全

1.什么是线程安全

在多线程环境下程序运行的结果与单线程环境下程序运行的结果不一样(不达预期)

2.线程不安全的原因

1.多个变量对一个共享变量做修改

2.线程是抢占式执行的,CPU的调度是随机的

3.原子性:代码没有一次性的执行完毕

4.内存可见性:线程1修改了共享变量的值,线程2不能及时获取到最新的值

5.有序性:由于指令重排序,指令无法按照书写的顺序进行执行.

3.JMM(Java内存模型)

JMM可以解决原子性,内存可见性,有序性的问题

1.主内存,进程启动时在主内存中申请内存资源,也就是内存条,用来保存所有的变量

2.工作内存,对应CPU中的缓存,每个线程都有自己的工作内存,工作内存互不影响,线程之间起到了内存隔离的效果

3.JMM规定,一个线程更新共享变量值的时候,必须先从主内存中加载到变量值到对应线程的工作内存中,修改完成后再将工作内存中的值刷新到主内存中.

4.synchronized锁

1.加锁之后可以将多线程(并发执行)变成单线程(串行执行).   ---解决了原子性

2.由于是单线程执行,线程2读到的值一定是线程1修改过的值  ---解决了内存可见性

3.不能解决有序性

synchronized的使用

1.可以修饰普通的方法  ---锁对象是new出来的对象(当前对象,this)

2.可以修饰静态的方法  ---锁对象是类对象,类名.class

3.可以修饰代码块          ---锁对象是指定的

5.锁对象

1.任何对象都可以充当锁对象,类对象,普通对象

2.锁对象的对象头中的markword区域记录当前争抢到锁的线程信息

3.多线程环境下判断是否发生锁竞争,只需要判断是否争抢的是一把锁.

6.volatile关键字

1.可以解决内存可见性      MESI缓存一致性协议和内存屏障

2.可以解决有序性

3.不能解决原子性

4.只能修饰变量

原子性可见性有序性
synchronizedYYN
volatileNYY

7.wait()和notify()

1.wait()是让线程进入休眠状态,notify()是唤醒等待的线程,都是Object中的类

2.wait()和notify()都会释放锁资源,wait()和notify()要对于同一个锁搭配使用.

 面试题:说一下wait()和sleep()的区别

1.本质上都是让线程等待,但是两个方法没什么关系

2.wait()是Object类中定义的方法,sleep()是Thread类中定义的方法

3.wait()必须与synchronized搭配使用,调用之后会释放锁.sleep()只是让线程进入堵塞等待,和锁没有什么区别

8.Java中线程安全的类

线程安全的类:Vector (不推荐使用)  Stack  HashTable (不推荐使用)  ConcurrentHashMap
StringBuffer

线程不安全的类:ArrayList   LinkedList   HashMap   TreeMap   HashSet   TreeSet   StringBuilder

二.单例模式

1.什么是单例

单例模式是一种设计模式(设计模式:就是在特定的场景下,解决问题最优的方式,类似于棋谱),单例:顾名思义,全局只有一个实例对象

2.怎么设计一个单例

1.口头约定

对外提供一个方法,规定使用者只能通过这个方法来获取这个类的实例对象(不靠谱,有些人就是new出来对象,不采用你这个方法获取对象)

2.使用编程语言的特性

Java中哪些对象时全局唯一的?

.class对象,比如Object.class   类对象

使用static修饰的属性,所有的实例对象访问的都是同一个类属性.

因此我们可以通过类对象和static配合的方式实现单例

实现单例有两种模式,饿汉模式和懒汉模式

三.饿汉模式

类的加载就完成初始化

public class Singleton {
    private static Singleton instance = new Singleton();

    private Singleton() {

    }

    public static Singleton getInstance() {
        return instance;
    }
}

优点:书写简单,不容易出错

四.懒汉模式

1.单线程下的懒汉模式

避免程序启动的时候浪费过多的系统资源,当程序使用这个对象的时候再对这个对象进行初始化

public class SingletonLazy {
    private static SingletonLazy instance = null;

    private SingletonLazy() {

    }

    public static SingletonLazy getInstance() {
        //在获取单例对象的时候,判断是否已经被创建,没有创建则创建
        if (instance == null) {
            instance = new SingletonLazy();
        }
        return instance;
    }
}

其实也不难写出,只需要在将instance初始化为null,当需要获取实例(getInstance)的时候,判断instance是否为null,为null就创建对象,否则直接返回.

这样我们就实现了懒汉模式的单例吗?当然以上的程序在单线程的情况下是正确的,但是多线程的情况下会出现一些问题.

2.多线程下的懒汉模式

模拟多线程的情况下通过以上程序获取实例对象:

public class Demo02_SingletonLazy {
    public static void main(String[] args) {
        //创建是个线程
        for (int i = 0; i < 10; ++i) {
            Thread thread = new Thread(() -> {
                //获取实例对象并打印
                SingletonLazy instance = SingletonLazy.getInstance();
                System.out.println(instance);
            });
            thread.start();

        }
    }
}

打印的结果:

由打印的结果可以看出,发现获取到的对象不一定都是同一个对象,不符合我们的预期,发生了线程不安全的现象

3.分析线程不安全的原因

刚开始的时候instance=null;

 可以观察出来,仅有两个线程的时候,就有可能有两个不同的对象.

其实我们仔细思考可以想出具有不同的对象只会发生没有对象的初期,后期对象不为空的时候,多个线程再进来,对象也不会发生改变了,但我们要求的是单例,所以我们要完全满足单例模式!

为了解决这个问题,我们可以给这个加锁

4.加锁解决这个问题

我们在if循环外面加上锁

public class SingletonLazy {
    private static SingletonLazy instance = null;

    private SingletonLazy() {

    }

    public static SingletonLazy getInstance() {
        synchronized (SingletonLazy.class) {
            //在获取单例对象的时候,判断是否已经被创建,没有创建则创建
            if (instance == null) {
                instance = new SingletonLazy();
            }
        }
        
        return instance;
    }
}

打印结果:

 我们考虑以下,是否能将synchronized包裹的代码块发到if语句的下面

public class SingletonLazy {
    private static SingletonLazy instance = null;

    private SingletonLazy() {

    }

    public static SingletonLazy getInstance() {
        //在获取单例对象的时候,判断是否已经被创建,没有创建则创建
        if (instance == null) {
            synchronized (SingletonLazy.class) {
                instance = new SingletonLazy();
            }
        }

        return instance;
    }
}

打印的结果:

是不可以的!Load和CMP操作没有原子执行,线程1加载到instace=null,线程2加载到的instace=null,所以线程1和线程2都是new出来一个新的SingletonLazy对象,所以要扩大加锁的范围

进行修改之后看起来就是正确的了,但是不高效.

5.双重检查锁

前面我们已经分析了,线程不安全的线程只存在于前期,也就是instace=null进入到方法的时期,instance!=null的时候是不会发生线程不安全的现象的,但是我们之前写的代码,不论在什么时期都要参与锁竞争,而锁竞争是十分耗费系统资源.

用户态:Java层面,JVM中执行的代码

内核态:执行的是CPU指令,加入synchronized后参与锁竞争就从用户态到内核态,而内核态执行效率没有用户态执行效率高.

来举一个现实中的例子:拿批改作业为例,同学们可以自己批改作业(用户态),也可以上交给老师进行批改(内核态),自己批改效率是很高的,但是交给老师,老师可能要备课,也要批改别人的作业,所以完成你作业的批改是十分不高效的.

public class SingletonLazy {
    private static SingletonLazy instance = null;

    private SingletonLazy() {

    }

    public static SingletonLazy getInstance() {
        if (instance == null) {
            //在获取单例对象的时候,判断是否已经被创建,没有创建则创建
            synchronized (SingletonLazy.class) {
                if (instance == null) {
                    instance = new SingletonLazy();
                }
            }
        }

        return instance;
    }
}

这种用双重if判断的方式叫做双重检查锁,两层if语句判断的目的不一样,外层if是判断是否为空,避免参与锁竞争,内层if判断是否为空,为instance进行赋值.

初始状态instance=null,外层if判断为true,进入,此时三个线程参与锁竞争,线程1拿到了锁,线程2和线程3处在堵塞状态,线程1内层if判断为true,执行instance的赋值操作,之后释放锁资源,返回赋值之后的对象,线程2拿到了锁资源,内层if判断为false,释放锁资源,返回instance,线程3拿到锁资源后,和线程2一样.

之后instance!=null,外层if判断为false,直接返回instance,不要参与锁竞争,提高了效率.

现在已经做到了确保原子性,内存可见性,但是还没有确保有序性,下面我们做进一步的优化

6.加volatile优化(单例懒汉模式最终版)

给共享变量instance加volatile关键字,可以避免指令重排序问题.

public class SingletonLazy {
    private static volatile SingletonLazy instance = null;

    private SingletonLazy() {

    }

    public static SingletonLazy getInstance() {
        if (instance == null) {
            //在获取单例对象的时候,判断是否已经被创建,没有创建则创建
            synchronized (SingletonLazy.class) {
                if (instance == null) {
                    instance = new SingletonLazy();
                }
            }
        }

        return instance;
    }
}

懒汉模式的优点:节约资源.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/513301.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

文心一言没有体验上,看看讯飞星火认知大模型

目录 前言 正文 1.简单对话环节 1.1什么是讯飞星火认知大模型 1.2你都可以干一些什么 1.3你的训练数据库还在持续更新么 1.4今天是哪一天 1.5宇宙中有哪些目前认为可能存在生命的星球 2.辅助学习对话 2.1我想完成一篇关于CT检测技术的论文综述&#xff0c;你有什么好的…

Elasticsearch - 聚合获取原始数据并分页排序模糊查询

文章目录 概述第一步 &#xff1a; 聚合获取原始数据并分页知识点&#xff1a;bucket_sort实现分页知识点&#xff1a;获取 total -----> cardinality 去重 小结第二步 分页并支持模糊查询方式一 query 方式方式二&#xff1a; 脚本cardinality 的 script 概述 ES版本&…

4.100ASK_V853-PRO开发板支持4寸MIPI屏

0.前言 ​ 由于之前我们已经适配过RGB屏&#xff0c;如果我们去适配了4寸MIPI屏&#xff0c;那么RGB屏就不能使用了。对于4寸屏购买链接为&#xff1a; 百问网4寸MIPI屏 LCD_调试指南:https://tina.100ask.net/SdkModule/Linux_LCD_DevelopmentGuide-01/ Display_开发指南:h…

pmp学习对职场的影响有多大?

不知不觉&#xff0c;2021的进度条已经走过了一半。行业政策的变化&#xff0c;岗位的能力对比&#xff0c;都让职场竞争变得更加激烈&#xff0c;成为一名优秀且具有竞争力的项目经理好像变得越来越难了。 为什么这样说呢&#xff1f;因为各行各业在对PM要求各项专业技能的同…

ChatGPT人工智能聊天机器人的优势与应用

当今社会&#xff0c;聊天机器人(Chatbot)已经成为一种新的交互方式。而ChatGPT则是一种基于深度学习技术的聊天机器人&#xff0c;它可以模拟人类对话的过程&#xff0c;可以为人们提供快速、便捷的解决方案。以下是ChatGPT的简介和优势&#xff1a; 基本信息&#xff1a;Chat…

将区块链技术融入电梯管理系统

电梯管理系统 电梯是人们日常生活和工作中必不可少的交通工具&#xff0c;为了保障电梯的安全性和运营效率&#xff0c;需要实时监测电梯状态及事件/故障。这里可以查考网络上提供的方案&#xff0c;在电梯内部安装了各种各样的传感器&#xff0c;通过网关将收集到的数据上传到…

小红书保姆级投放指南 | 助力引爆618

5月到来&#xff0c;618营销正式进入放量冲刺期&#xff0c;作为年中重量级营销节点&#xff0c;各大品牌角逐激烈。 本期千瓜推出618投放指南&#xff0c;助力品牌在冲刺期抢占用户心智、抓住高转化周期流量&#xff0c;打赢这场无硝烟之战。整体节奏把控四个阶段循序渐进 品牌…

Acrel-2000系列监控系统在亚运手球比赛馆建设10kV供配电工程中的应用

安科瑞 耿敏花 摘要:智能化配电监控系统是数字化和信息化时代应运而生的产物&#xff0c;已经被广泛应用于电网用户侧楼宇、体育场馆、科研设施、机场、交通、医院、电力和石化行业等诸多领域的高/低压变配电系统中。安科瑞自研的Acrel-2000系列监控系统可监控高压开关柜、低压…

第八章 SSM整合

1.整合关键点 Spring&#xff1a;负责对象的创建、维护、管理及对象依赖资源的注入 SpringMVC&#xff1a;负责请求的处理相当于(Servlet) MyBatis&#xff1a;负责与数据库进行交互 2.整合步骤 2.1.在pom.xml文件中导入依赖 mybatis、spring-webmvc、mybatis-spring、bonecp数…

GIF动态图录制工具

大家好&#xff0c;我是小寻&#xff0c;欢迎关注公众号:工具优选&#xff0c;免费领取优质项目源码和常用工具&#xff0c;还可以加入我的交流群! 一、工具介绍 Screen to Gif中文版是一款方便可靠的gif动画录制软件&#xff0c;可以用来快速录制屏幕上的指定区域&#xff…

二十年前的老游戏,为何再次让无数程序员痴迷不已?

SpaceTraders是个古老的策略类游戏&#xff0c;运行在古老的Palm OS和Windows Mobile PDA上。 游戏开始时&#xff0c;玩家将获得一艘飞船&#xff0c;然后驾驶它在各个星球之间穿梭&#xff0c;挖掘星球矿产&#xff0c;低买高卖赚取利润&#xff0c;赚了钱可以升级飞船&#…

麻了,一个操作把MySQL主从复制整崩了

最近公司某项目上反馈mysql主从复制失败&#xff0c;被运维部门记了一次大过&#xff0c;影响到了项目的验收推进&#xff0c;那么究竟是什么原因导致的呢&#xff1f;而主从复制的原理又是什么呢&#xff1f;本文就对排查分析的过程做一个记录。 主从复制原理 我们先来简单了…

淘宝商品详情接口 淘宝商品库存接口 淘宝商品销量接口 淘宝商品sku信息接口 淘宝商品优惠价接口

淘宝商品详情API接口item_get是一个非常重要的API接口&#xff0c;它可以获取淘宝商品的详细信息。对于淘_宝卖家来说&#xff0c;通过调用该接口可以实现对自己商品信息的获取、修改和管理等功能。 使用item_get接口可以获取一个商品的所有信息&#xff0c;包括商品的标题、价…

[学习笔记] [机器学习] 4. [上]线性回归(正规方程、梯度下降、岭回归)

视频链接数据集下载地址&#xff1a;无需下载 本文学习目标&#xff1a; 掌握线性回归的实现过程应用LinearRegression或SGDRegressor实现回归预测知道回归算法的评估标准及其公式知道过拟合与欠拟合的原因以及解决方法知道岭回归的原理及与线性回归的不同之处应用Ridge实现回…

这次彻底不需要账号了,无需魔法永久白嫖GPT

免费GPT 自GPT风靡以来&#xff0c;大家用的是不亦乐乎&#xff0c;你用他去解决过实际问题&#xff0c;你用他去写过代码&#xff0c;你用他去修改过bug&#xff0c;你用他去写过sql&#xff0c;你用他去画过图&#xff0c;你问过他你能想到的任何“刁钻”问题。 你&#xff…

如何在没有密码的情况下解锁华为手机

华为手机用户通常会使用密码保护他们的设备免受未经授权的访问。但是当用户忘记密码时就会出现问题。如果您无法回忆起密码&#xff0c;可以选择重置手机。但是有更多更好的方法可以帮助您解锁华为手机。在本文中&#xff0c;我们将向您展示如何免密码解锁华为手机。按照本文&a…

“五位一体”打造数字业务安全体系

顶象联合中国信通院发布的《业务安全白皮书—数字业务风险与安全》显示&#xff0c;随着数字化的发展&#xff0c;企业的关键数据、用户信息、基础设施、运营过程等均处于边界模糊且日益开放的环境中&#xff0c;涉及利益流和高附加值的业务面临多样的安全隐患&#xff1b;同时…

Linux下最强安卓模拟器,流畅又丝滑(附详细安装教程)此瓜保熟|Linux游戏党

我打算完全从头开始&#xff0c;写一个专门用于桌面办公的纯国产操作系统 &#xff0c;规避主流操作系统上影响用户体验的问题&#xff0c;系统力求简洁。有兴趣加QQ群&#xff1a;709652950 好东西让更多人发现&#xff01;我找了整整两年&#xff0c;什么Anbox&#xff0c;什…

【必知必懂论文】之多模态实体识别

引言 命名实体识别&#xff08;NER&#xff09;是自然语言处理(NLP)领域中的最基础、最核心的任务之一&#xff0c;该任务旨在识别出文本中的命名实体&#xff08;通常指特定类型事物的名称或符号&#xff0c;一般是一个名词或者短语&#xff09;&#xff0c;并将识别出的实体…

【这七款网工在线画拓扑工具,你会用几个呢?】

其实绘制拓扑图的工具有很多&#xff0c;今天主要推荐给大家7款在线的绘图软件&#xff0c;不仅好用&#xff0c;不占内存&#xff0c;而且功能强大。 看看有没有你种草的那一款哈&#xff0c;当然&#xff0c;如果有其他更好用的工具&#xff0c;也欢迎留言区告诉其他网工朋友…