面试易考:多线程模式下的单例模式两种具体实现(饿汉,懒汉),两个的线程安全性,阻塞队列,生产者消费者模型

news2024/11/25 2:21:55

补充:synchron(锁对象):给对象里面做了一个标记,每个对象,除了代码中写的属性外,此外还有一部分空间,存储的是标志位,这个标志位相当于是加锁,当这一位被标记加锁之后,此时其他线程也想对这个对象标识,就会进行阻塞等待。

 

面试小技巧:某某hr问,有没有女朋友,闭眼睛猛猛答没有女盆友,拒绝奇奇怪怪的送命题

     给你50w啥的你打算干什么——公司附近买房 🌝​​​​​​​ 🌝​​​​​​​ 🌝                    

  (下面的两个解决线程安全性这块还会有一部分技巧,请坚持看完,并尽量转发给你的朋友)


 一、💛

初步介绍,面试的两个重点

1.单例模式:一种设计模式(设计模式:介绍了很多典型场景的处理方式,按模式写代码,不会写的很烂)

有时候希望对象,在整个线程中只实例化一次(也就是只new一次)(那么如何保证只new一次呢,靠程序猿们的嘴咩🌚🌚,男人的话不可信,所以是让编译器,去进行更严格的检查)

2.工厂模式(以后会写)

二、  💙

单例模式的细分:

1.饿汉模式(迫切):程序启动,我就立马new个床照实例。

2.懒汉模式(延时):第一次使用实例的时候,再创建,否则能不建立就不建立。

两个哪个好,也是分情况

假如一共整个大文件40G

饿汉是会一次尝试吧所有内容加载完毕,再显示出来(半小时体验卡)

懒汉是只加载一部分内容,其他部分,用户翻页用到了再去加载。

三、💜

饿汉的代码实现(一键缓存)

获取实例一定就是上面的那个实例,但是一定不能创建对象吗,其实也不是,反射可以去操作来创新(反射如同小门一样,但是不推荐用)

import java.util.Scanner;

class Singleton{
    private  static  Singleton instance=new Singleton();   //让他变成类属性
    public  static  Singleton getInstance(){               //类方法,这俩个都是类被加载
        return instance;                                     就开始使用
    }
    private  Singleton(){}               //可以让他不再new对象,要是new就报错
}
public  class  Demo {
    public static void main(String[] args) throws InterruptedException {
        
        System.out.println(Singleton.getInstance());
        
        
    }
}

懒汉模式。 他需要首次调用getInstance()才会new,也就是一定是比饿汉慢的

import java.util.Scanner;

class SingletonLAZY{
    private  static  SingletonLAZY instance;
    public  static  SingletonLAZY getInstance(){           //不去调用getInstance这个方法
        if(instance==null){                              就不去去new这个对象
            instance=new SingletonLAZY();
        }
        return instance;
    }
    private  SingletonLAZY(){}                           //不让你去new对象,否则报错
}
public  class  Demo {
    public static void main(String[] args) throws InterruptedException {

        Singleton s1=Singleton.getInstance();
        Singleton s2=Singleton.getInstance();
        System.out.println(s1==s2);


    }
}


四、❤️

(情况1多线程的线程安全,多个线程调用setInsetance(务必会读(辛可肉耐子)会写)情况下,哪个代码是线程安全的

1.饿汉模式,多线程下读取instance内容,多线程读取同一个变量没有问题

2.懒汉:懒汉就会出现些问题,我那个上面的判断是否是空,然后再new个对象,但是他并不是原子性的啊(不知道啥原子性,看我前面的博客哦家人。)那么多线程情况下就会如下图所示. xx-instance.   yy-Singletion

当然某某盆友说,第二次的操作,不就是把原来的instance的引用给修改了,之前new出来的对象不是立刻就被JVM的gc回收了吗(垃圾回收机制)。最后不还是只剩一个对象了咩?

但实际上new 一个对象可能开销非常大!可能服务器启动100G内存中,都是由一个对象管理的,可能new一个花费20分钟

那么我们该如何解决呢?

首先和我们之前的处理方式一样,把if和new放到一个整体,

class Singleton{
    private  static  Singleton instance;
    public  static  Singleton getInstance() {
        synchronized (instance) {                 //把他们两个加锁
            if (instance == null) {
                instance = new Singleton();
            }
            return instance;
        }
    }
    private  Singleton(){}
}

这也就意味着:刚才发的图的情况,会进行阻塞,也就让这个线程没有了上述的bug。

加锁成本高,可能引起阻塞等待,无脑加锁会导致程序执行效率受到影响。

(vector,HashTable,StringBuffer,也不是很常用,他们的关键方法都用了synchronized,无论单、多线程,是否有线程安全,都会有🔒,也会影响效率)

(情况1号改进衔接2号但是像这个代码一样,每次都调用getInstance都要加锁,但是这样有必要吗?

线程不安全,主要是首次new对象才有问题,一旦对象new成功了,后续调用getInstance都是安全的。

改进:

class Singleton{
    private  static  Singleton instance;
    public  static  Singleton getInstance() {
        if(instance==null){                   //先判断是否需要创建对象
        synchronized (instance) {             //下面这两个才可能是线程安全的主要问题,加锁
            if (instance == null) {
                instance = new Singleton();
            }
        }
    }
        return instance;                     //不需要就,直接返回,没有什么线程安全问题
    }
    private  Singleton(){}
}

有人可能说同一个条件,写两遍合理吗?

之前是没有阻塞,但现在多线程,第二个条件和一个条件之间间隔非常长的时间,这个间隔,可能别的线程就把instance改变了

(情况1号衔接2号同时考虑3号情况)

两个线程

第一个线程修改完instance之后,就结束了代码块,释放了锁

第二个线程能够从中获取到锁从阻塞中恢复了

但是if(instance==null),t2的读操作一定会读到1线程修改的值吗?内存可见性问题,此处就需要给instance加上volatile(会读会写 (wao(平🐍)里太哦)

这样是更加稳妥的,只有这样才可以避免风险,毕竟编译器优化还是有点离谱的,是否会去触发优化,不好说,但是加上volatile更稳妥

并且volatile还有另外的用途,避免此处赋值操作进行指令重排序

如果单线程则不会出现指令重排序问题,但是多线程可能就会出现问题了(也是编译器优化的一种手段,保证原有执行的逻辑程序的情况下,对代码执行效率顺序调整,使执行效率提高。

instance=new SingletonLazy()分成3步

1.把对象创建出内存空间,得到内存地址

2.把空间调用构造方法,对对象进行初始化。

3.把内存地址,复制给instance重引用。

这三步可能进行重排序->单个线程先2后3,先后无所谓

但是假如t1先进行第一步,在进程第三步,第二步还没做的时候,出现了线程切换,此时还没给对象初始化就调度到其他线程了。这样t2在判读是否为空,直接返回instance,并且后续假如一直使用instance的一些属性或者方法,instance是没有被初始化啊,不可靠,不能随便用,否则什么🐮🐎情况都蹦出来了。

用volatile(保证多线程环境下的可见性,有序性)一定按123执行,此时不让他这么优化,也就不会产生重排序。

1️⃣加锁

2️⃣双层if

3️⃣volatile

面试小技巧(全是套路):考官让写这个懒汉饿汉什么的(装思考一会🤔),别一下把全优化的写上去,先装傻,写那个不完全的版本,然后他在说如何如何,再去一步一步慢慢改,让他觉得这个考题,不是你提前准备的,然后他就会觉得,小伙脑子好使,灵光


五、💚 

阻塞队列(有阻塞功能的队列,下一章或者下两章是如何实现)

1.当队列满的时候,继续入队列,就会出现阻塞,阻塞到其他线程从队列中取起元素为止。

2.当队列空的时候,继续出队列,也会出现阻塞,阻塞到其他线程往队列添加元素为止

用处非常大,也很重点

通过阻塞队列可以实现生产者,消费者模型

如包饺子

生产者:整饺子皮

消费者:包饺子

放饺子的桌子就是交易场所。

六、 💓

生产者,消费者模型优势

1.解耦合(两个关系块的关系叫耦合):降低模块之间的联系

 改进引入阻塞队列:

此时,A和B就通过阻塞队列很好的解耦合了~此时如果A或者B挂了,由于他们彼此之间,不会直接交互,没有啥太大影响,如果新增服务器C,此时A服务器完全不需要任何修改,只需要让他从队列中取元素就可以。 

2.削峰填谷:服务器收到来自客户端/用户的请求,不是一成不变的,可能因为一些突发事件,引起请求数目暴增。(如某某明星恋情,干翻微博),正常情况下同一时刻请求的数量都是有限的。

一个分布式系统,有的机器承担压力大,有的机器承担压力小 

​​​​​​​此时A收到一个请求,B也需要立刻去处理这一个请求,如果A能承受的压力大,B能承受的压力小的话,此时,很可能B就先挂了

但假如说用上,上面的阻塞队列

 削峰:当外界的请求突然暴涨,A收到的请求多了,A就会给队列中写入更多的请求数据,但是B可以依旧按照自己的节奏处理,B不至于挂了,相当于队列起到了缓冲的作用。

填谷:峰值多的时候只是暂时的~当峰值消退的时候,A收到的请求少了,B还按照原有速率去处理,不至于他特别空闲(填谷)

七、💗

JAVA提供了现成的阻塞队列

BlockingDeque:阻塞队列提供的接口,需要具体实例化类 

BlockingQueue<String>queue=new ArrayBlockingQueue<>(3);//基于数组(最好提前知道他多少个元素,避免频繁扩容)

BlockingQueue<String>queue2=new PriorityBlockingQueue<>(3);//基于优先级队列(就是堆),有优先级就选它
BlockingDeque<String>queue1=new LinkedBlockingDeque<>(3);;//基于链表(不知道多少个元素使用链表,因为要修改

put tabke带阻塞功能

offer poll不带阻塞功能,

基于这个写个生产者消费者模型

import java.util.Scanner;
import java.util.concurrent.*;

public  class  Demo {
    public static void main(String[] args)  {
        BlockingDeque<Integer>queue1=new LinkedBlockingDeque<>();
        Thread t1=new Thread(()->{
            int count =0;
            while (true){
                try {
                    queue1.put(count);           //t1把生产的数字放入队列中
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("生产了元素"+count);   
                count++;
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        Thread t2=new Thread(()->{
            while(true) {
                Integer n = null;
                try {
                    n = queue1.take();          //t2接受他们t1线程往队列里填的数
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("消费者" + n);
            }
        });
        t1.start();
        t2.start();
    }
}

 

 

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

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

相关文章

项目实战 — 消息队列(7){虚拟主机设计(2)}

目录 一、消费消息的规则 二、消费消息的具体实现方法 &#x1f345; 1、编写消费者类&#xff08;ConsumerEnv&#xff09; &#x1f345; 2、编写Consumer函数式接口&#xff08;回调函数&#xff09; &#x1f345; 3、编写ConsumeerManager类 &#x1f384;定义成员变…

Linux系统下安装配置 Nginx 超详细图文教程

Linux系统下安装配置 Nginx 详细教程介绍 一、下载 Nginx 安装包 打开Nginx官网 &#xff1a;http://nginx.org/en/download.html 然后我们找到一个版本&#xff0c;把鼠标移动到上面&#xff0c;右键 - 复制链接地址 我们使用 wget 命令把Nginx安装包下载到/usr/local/目录中…

微信小程序实现当前页面更新上一个页面

日常项目中需要实现的一个价格脱敏功能&#xff1a;通过点击页面二中的查看完整信息 点击回退按钮实现页面一中的价格显露出来 通过查询了大量资料发现 大多数都是通过调用上一个接口的onload 或者onshow 实现视图更新 经测试后 发现 无法实现 只能更改数据 无法更新视图 实现…

shell脚本条件测试语句,if,case

shell脚本条件测试语句&#xff0c;if&#xff0c;case 一.条件测试1.1test命令1.2文件测试1.2.1文件测试常见选项 1.3数值比较1.4字符串比较1.5逻辑测试 二.if语句2.1单分支结构2.3多分支 三.case语句 一.条件测试 1.1test命令 测试特定的表达式是否成立&#xff0c;当条件成…

InVEST模型使用

第一天&#xff1a; 1. 生态系统服务理论联系实践案例讲解 2. InVEST模型的开发历程、不同版本的差异及对数据需求的讲解 3. InVEST所需数据的要求&#xff08;分辨率、格式、投影系统等&#xff09;、获取及标准化预处理讲解 4. InVEST运行常见问题及处理解决方法讲解 5.…

【C++】数据结构与算法:常用查找算法

&#x1f60f;★,:.☆(&#xffe3;▽&#xffe3;)/$:.★ &#x1f60f; 这篇文章主要介绍常用查找算法。 学其所用&#xff0c;用其所学。——梁启超 欢迎来到我的博客&#xff0c;一起学习&#xff0c;共同进步。 喜欢的朋友可以关注一下&#xff0c;下次更新不迷路&#x1…

【果树农药喷洒机器人】Part6:静态PWM变量喷药实验

文章目录 一、引言二、静态PWM变量喷药实验2.1搭建喷药实验平台2.2变量喷药控制实验 一、引言 为综合评估所设计的果树喷药机器人变量喷药效率和质量&#xff0c;验证系统的控制性能和实际作业的可行性&#xff0c;本章开展果树变量喷药实验。首先&#xff0c;通过静态的PWM变…

Mongodb 分页查询数据重复

&#xff08;学习的本质&#xff0c;不在于记住哪些知识&#xff0c;而在于它触发了你的思考——迈克尔桑德尔&#xff09; mongodb排序的限制 相关链接 最多对32个键进行排序排序一致性 其中排序一致性指的是&#xff0c;如果被排序的字段值是重复的&#xff0c;那么在进行…

不同路径数

希望这篇题解对你有用&#xff0c;麻烦动动手指点个赞或关注&#xff0c;感谢您的关注~ 不清楚蓝桥杯考什么的点点下方&#x1f447; 考点秘籍 想背纯享模版的伙伴们点点下方&#x1f447; 蓝桥杯省一你一定不能错过的模板大全(第一期) 蓝桥杯省一你一定不能错过的模板大全…

C指针:程序员的神奇箭头,穿越内存的冒险之旅!

目录 &#x1f575;️‍♂️ 引言&#xff1a;指针&#xff0c;那些指向星星的小箭头&#xff01; 一、&#x1f3af; 探索箭头&#xff1a;指针的基础知识 1.1 指针是什么&#xff1f; 1.2 解引用操作符&#xff1a;* 是关键 1.3 指针的比较和运算 1.4 空指针&#xff1a…

Gopeed-全平台开源高速下载器 支持(HTTP、BitTorrent、Magnet)协议

一、软件介绍 Gopeed&#xff08;全称 Go Speed&#xff09;&#xff0c;是一款由GolangFlutter开发的高速下载器&#xff0c;开源、轻量、原生&#xff0c;支持&#xff08;HTTP、BitTorrent、Magnet 等&#xff09;协议下载&#xff0c;并且支持全平台使用&#xff0c;底层使…

淘宝商品详情接口(商品列表,APP详情接口)返回示例说明,PC端和APP端

淘宝商品详情包括以下信息&#xff1a; 1. 商品标题&#xff1a;商品的名称或标题&#xff0c;用于描述商品的特点和功能。 2. 商品价格&#xff1a;商品的销售价格&#xff0c;包括原价和促销价等。 3. 商品图片&#xff1a;展示商品的照片或图像&#xff0c;以便顾客可以更…

Pinia的使用,只需四步轻松上手

Pinia与vuex相比&#xff0c;少了mutation和命名空间&#xff0c;支持ts。更适配vue3. 基本使用&#xff1a; 1.创建一个store文件夹&#xff0c;引用createPinia()方法并暴露出去&#xff08;图一&#xff09; 2.main.ts下引用createPinia并注册use一下&#xff08;图2&#x…

SpringBoot启动报错:java: 无法访问org.springframework.boot.SpringApplication

报错原因&#xff1a;jdk 1.8版本与SpringBoot 3.1.2版本不匹配 解决方案&#xff1a;将SpringBoot版本降到2系列版本(例如2.5.4)。如下图&#xff1a; 修改版本后切记刷新Meavn依赖 然后重新启动即可成功。如下图&#xff1a;

【Matlab】PSO优化(单隐层)BP神经网络

上一篇博客介绍了BP-GA&#xff1a;BP神经网络遗传算法(BP-GA)函数极值寻优——非线性函数求极值&#xff0c;本篇博客将介绍用PSO&#xff08;粒子群优化算法&#xff09;优化BP神经网络。 1.优化思路 BP神经网络的隐藏节点通常由重复的前向传递和反向传播的方式来决定&#…

00 - 环境配置

1. 环境说明 使用git gitee 2. 安装配置 ubuntuVM-8-3-ubuntu:~/wuxiang/git$ git --version git version 2.25.1 ubuntuVM-8-3-ubuntu:~/wuxiang/git$2.1 配置user信息 ubuntuVM-8-3-ubuntu:~/wuxiang/git$ git config --global user.name wuxxxxx ubuntuVM-8-3-ubuntu:~…

【遍历】非递归法 二叉树的前中后序遍历

文章目录 非递归法前序遍历后序遍历中序遍历 递归法DFS 非递归法 通过栈Stack来模拟递归。 前序遍历 LeetCode 144 前序遍历&#xff1a;1 2 3 定义&#xff1a;存放答案的List、栈Stack 将root入栈出栈&#xff1a;node&#xff0c;为null则舍弃将node放入list将node.r…

【Spring】-Spring项目的创建

作者&#xff1a;学Java的冬瓜 博客主页&#xff1a;☀冬瓜的主页&#x1f319; 专栏&#xff1a;【Framework】 主要内容&#xff1a;创建spring项目的步骤&#xff1a;先创建一个maven项目&#xff0c;再在pom.xml中添加spring框架支持&#xff0c;最后写一个启动类。 文章目…

程序猿成长之路之密码学篇-分组密码加密模式及IV(偏移量)的详解

Cipher.getInstance("AES/ECB/PKCS5Padding"); Cipher cipher Cipher.getInstance("AES/CBC/PKCS5Padding"); 在进行加解密编程的时候应该有很多小伙伴接触过以上的语句&#xff0c;但是大伙儿在编码过程中是否了解过ECB/CBC的含义、区别以及PKCS5Padding…

学生公寓一进四出智能电表的功能介绍

学生公寓一进四出智能电表石家庄光大远通电气有限公司模块时间控制功能&#xff1a;可设定每个宿舍自动断电和供电的时间&#xff1b;也可以设定某时间段内为小功率输出,设定时间后自动恢复正常供电。权限管理&#xff1a;管理者可对操作人员设定不同操作权限&#xff1b; 软件…