Java编程--单例模式(饿汉模式/懒汉模式)/阻塞队列

       前言 

        逆水行舟,不进则退!!!     


       目录

       单例模式

        饿汉模式:       

        懒汉模式:

       什么是阻塞队列

        什么是高内聚 低耦合

       阻塞队列的实现


       单例模式

        单例模式(Singleton Pattern)是一种常见的设计模式,主要应用于创建型模式。它确保一个类只有一个实例,并且自行负责实例化并向整个系统提供这个唯一实例。此外,这种模式属于创建型模式,通过这种方式创建的类在当前进程中只存在一个实例。

        在计算机系统中,诸如线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例,因为它们在整个应用程序中只需要一个实例。然而,尽管单例模式是应用最广的设计模式之一,也是程序员非常熟悉的一种设计模式,但它仍然存在一些需要注意的问题。

        饿汉模式:       
			
//饿汉模式单例模式
//此处保证Singleton这个类只能创建出一个实例
class Singleton{//在此处,先把这个实例给创建出来private static Singleton instance = new Singleton();//获取这个唯一实例public static SingletongetInstance() {return instance;}//为了避免Singleton类不小心被复制出多份来,//把构造方法设为private,在类外面,就无法通过new的方式来创建这个Singleton实例了private Singleton() {  }}public class ThreadDemo6 {public static void main (String[] args) {Singletons1 = Singleton.getInstance();Singletons2 = Singleton.getInstance();System.out.println(s1==s2);}
}

        懒汉模式:
//懒汉模式
class SingletonLazy{// 饿汉 与 懒汉 的 根本区别在这里private static SingletonLazy instance = null;public static SingletonLazygetInstance() {if (instance == null) {instance = new SingletonLazy ();}return instance;}private SingletonLazy() {};}public class ThreadDemo7 {public static void main (String[] args) {SingletonLazys1 = SingletonLazy.getInstance();SingletonLazys2 = SingletonLazy.getInstance();System.out.println(s1==s2);}
}

      

        懒汉模式中有一个new的操作,这里就可能造成线程安全问题,而饿汉模式中只有读的操作,所以是线程安全的。

         懒汉模式的线程不安全 是因为读操作、比较、写操作不是原子性的。解决的办法就是将这三个操作上锁,使其变成原子性的。

        在懒汉模式中,实例化对象这个步骤可分为下图中的三步:

        不过,也是有办法解决的:volatile,volatile有两个功能:1,解决内存可见性;2,禁止指令重排序。

        或者使用 synchronized 来解决指令重排序问题。

//懒汉模式
classSingletonLazy{private volatile static SingletonLazy instance = null;public static SingletonLazy getInstance () {if (instance == null ) {        //外层这个if 是判断是否加锁synchronized (SingletonLazy.class) {if (instance == null) {      // 内层这个if  是判断是否实例化对象instance = new SingletonLazy();}}}return instance;}private SingletonLazy () {};
}public class ThreadDemo7 {public static void main (String[] args) {SingletonLazys1 = SingletonLazy.getInstance();SingletonLazys2 = SingletonLazy.getInstance();System.out.println(s1 == s2);}
}

针对上面代码中提一个问题:既然外层的if已经判断为null了, 又有加锁操作,是否可以将内存的判断删掉呢?

        答:假设有t1、t2两个线程,两个线程都执行到了外层的if语句,并且判断都为null,但是呢,t1线程比t2线程略微快那么一点儿,t1线程先申请到了锁,所以t2线程就只能线程阻塞了,等到t1线程执行完毕后,已经实例化了对象,此时t2线程再去执行,这时的内层if语句就会将t2线程拦下。所以说,两个判断语句,一个都不能少。

        

这里还是有一个问题: 比如t1线程通过synchronized已经对实例化操作进行加锁了,其他线程又如何将执行到一半的t1线程给切换走呢?

        答:线程加锁,并不是说这个线程就一直占用cpu,只是其他线程执行被上锁的代码时会阻塞,但是线程的切换调度还是照常进行的,并不会因为加锁而改变。(意思就是,已经上锁了,你的就是你的,别人拿不走,但是你先别着急,我的这个任务优先级更高,急需执行,所以你就先阻塞一下)。然而问题就是出现在这里,我t1线程执行到一半了,instance已经是非null了,但是还没有构造出对象。结果阻塞了,然后其他线程刚好在t1阻塞这个期间执行到了第一个if语句,发现instance不为null,结果就直接返回了instance,这个就导致了instance引用指向的对象不完整,引发后续问题。


       什么是阻塞队列
        什么是高内聚 低耦合

                答:高内聚,这是一个软件工程中的重要概念,它是判断软件设计优劣的标准之一。具体来说,高内聚是指模块内部的元素关联性非常强,以至于模块的单一性非常显著。理想情况下,一个模块应尽可能独立地完成某个特定的功能。

                在面向对象的设计中,高内聚低耦合是一个重要的设计原则,其主要目标是增强程序模块的可重用性和移植性。如果一个模块的内部实现过于复杂,可能会影响其可重用性和移植性。例如,如果一个模块需要被各种场景引用,那么代码的质量可能会变得非常脆弱,这种情况下建议将该模块拆分为多个独立的模块。

                总的来说,高内聚是强调模块功能的独立性和完整性,而低耦合则是强调模块之间的相互独立性,它们共同构成了软件设计的重要原则。

        阻塞队列是一种在多线程编程中经常使用的线程同步工具,它的主要功能是控制生产者和消费者之间的数据流量。阻塞队列的“阻塞”特性带来了几个显著的优点:

                1. 内存消耗可控:当队列容量有限时,内存消耗也会受到限制,防止过度消耗系统资源。

                2. 平衡生产者和消费者速度:阻塞功能使得生产者和消费者两端的能力得以平衡。当有任何一端速度过快时,阻塞队列便会把过快的速度限制下来。

                3. 自动线程阻塞与唤醒:在队列中没有数据的情况下,消费者端的所有线程都会被自动阻塞(挂起),直到有数据放入队列。这样的机制减少了手动管理线程的复杂性。

                4. 指定超时等待:在获取元素时,如果队列为空,线程可以等待特定的超时时间。如果在超时时间内有数据加入队列,线程将继续执行;否则,可以选择放弃等待或采取其他补救措施。

                5. 缓冲区长度可调节:一般的队列只能是有限长度的缓冲区,一旦超出缓冲长度,就无法保留了。当阻塞队列满时,阻塞队列会通过阻塞保留住当前想要继续入队的任务。

        总体而言,阻塞队列通过其阻塞特性,不仅使线程间的通信更加高效,而且减少了程序员需要处理的并发问题,大大提高了程序的稳定性和可靠性。

阻塞队列为生产者消费者模型 带来了两个好处:

        1)解耦合;

                简单理解:就是现在有AB两个程序,其中任何一个程序崩了,都会导致另一个程序的崩溃,这种就是高耦合现象,处处受其他程序掣肘。

                阻塞队列的出现就是来降低两个程序之间的耦合,将AB程序之间的信息交流通过阻塞队列来实现,这样的话,任何一个程序挂了,但是阻塞队列还好着,另一个程序还是可以从队里中拿到其的请求。

        2)削峰填谷

                "阻塞队列的削峰填谷"是一个在多线程编程中常用的术语,主要用于描述阻塞队列在处理并发任务时的一种重要功能。

                1. 削峰:这是指当并发任务的数量过多,以至于系统无法及时处理时,阻塞队列可以起到缓冲的作用,避免系统因为瞬时的大量任务而崩溃。阻塞队列相当于一个缓冲区,平衡了生产者和消费者的处理能力。

                2. 填谷:当并发任务的数量较少时,阻塞队列还可以存储这些暂时没有处理的任务,等到后续有新的任务到来时,再一起处理。这样可以避免系统的空闲资源浪费,提高了系统的处理效率。


       阻塞队列的实现

//自己实现阻塞队列
//此处不考虑泛型, 直接使用 int 来表示class MyBlockingQueue {private int[] items = new int[1000];private int head = 0;   // 头指针private int tail = 0;   // 尾指针private int size = 0;   // 记录阻塞队列中元素的个数//入队列public void put(int value) throws InterruptedException {synchronized(this) {//在这里进行循环等待,因为 wait 可能被打断。while (size == items.length) {//队列满了,不能继续插入this.wait();// 自动阻塞,等待出队列代码(消费者)的 唤醒}//数组实现循环队列的处理items[tail] = value;tail++;//求余数 是一种解决循环数组的方法//tail = tail % items.length;//也可以进行一个判断 来解决//相比求余数,判断语句的解决方式更容易看懂。 并且这种代码的效率可能更高。if (tail >= items.length) {tail = 0;}size++;// 当前队列不为空了,唤醒 正在阻塞等待 的出队列程序(消费者线程)this.notify();}}// 出队列public Integer take() throws InterruptedException {int result = 0;synchronized(this) {//这里也是循环等待,做同样的处理while (size == 0) {//队列为空, 无法取出数值// 自动阻塞,等待入队列程序(生产者线程)的唤醒this.wait();}result = items[head];head++;if (head >= items.length) {head = 0;}size--;// 此时队列中 不是满着的, 唤醒 入队列程序(生产者线程)this.notify();}return result;}//1, 先要保证线程安全}public class ThreadDemo9 {public static void main(String[] args) {MyBlockingQueue queue = new MyBlockingQueue();//消费者线程Thread customer = new Thread(() -> {while(true) {Integer result = null;try {result = queue.take();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("消费元素:" + result);}});//生产者线程Thread producter = new Thread(() -> {int count = 0;while(true) {try {queue.put(count);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("生产元素:  " + count);count++;try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}}});customer.start();producter.start();}
}


        我是专注学习的章鱼哥~

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

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

相关文章

WorldView 1 2 3 4卫星影像

WorldView WorldView卫星是Digitalglobe公司的商业成像卫星系统。它由两颗(WorldView-I和WorldView-II)卫星组成。 WorldView-1 WorldView-1卫星为美国DigitalGlobe公司的高分辨率商用卫星,于2007年9月18日成功发射,可提供0.5m分辨率卫星影像。灵活的…

设计模式之适配器(Adapter)

Adapter Wapper 接口转换器 如果一个类不能直接访问另一个类的时候,中间加一个Adapter转换器就能访问了 常见例子: 电压转接头 java.io jdbc-odbc bridge(不是桥接模式) ASM Transformer java io里面的读文件操作: FileInputStream是字节流读文件,就像…

2023-2024-2 高级语言程序设计-二维数组

7-1 矩阵运算 给定一个nn的方阵&#xff0c;本题要求计算该矩阵除副对角线、最后一列和最后一行以外的所有元素之和。副对角线为从矩阵的右上角至左下角的连线。 输入格式: 输入第一行给出正整数n&#xff08;1<n≤10&#xff09;&#xff1b;随后n行&#xff0c;每行给出…

深入理解Kafka3.6.0的核心概念,搭建与使用

Kafka是最初由Linkedin公司开发&#xff0c;是一个分布式、支持分区的&#xff08;partition&#xff09;、多副本的&#xff08;replica&#xff09;&#xff0c;基于zookeeper协调的分布式消息系统&#xff0c;它的最大的特性就是可以实时的处理大量数据以满足各种需求场景&a…

桌面便签软件用哪个?10款全球好用的便签软件推荐,告别杂论无章!

在如今的快节奏社会中&#xff0c;我们的生活和工作节奏越来越快&#xff0c;每天面对的信息成倍地增长。有时候&#xff0c;我们需要随手记下一些重要的事情&#xff0c;或者是一些突然的灵感&#xff0c;这时候就需要一款好用的桌面便签软件。 桌面便签软件可以帮助我们更好…

LeetCode(13)除自身以外数组的乘积【数组/字符串】【中等】

目录 1.题目2.答案3.提交结果截图 链接&#xff1a; 238. 除自身以外数组的乘积 1.题目 给你一个整数数组 nums&#xff0c;返回 数组 answer &#xff0c;其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素…

前端NaN解决方案

// 2.3 函数表达式可以传递参数还可以有返回值&#xff0c;使用方法和前面具名函数类似let sum function (x, y) { // 形参xx||0yy||0return x y}let re sum() // 实参console.log(re) // 3 function sum(x 0, y 0) {return x y}console.log(sum()) // 0console.log(s…

HTML+CSS、Vue+less+、HTML+less 组件封装实现二级菜单切换样式跑(含全部代码)

一、HTMLCSS二级菜单 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>Document</title><…

mysql之正则表达式匹配

题目&#xff1a; 今天在牛客网看到一道关于数据库正则表达式匹配的问题&#xff0c;发现自己一点不会做。 正则表达式&#xff1a; 一、正则表达式 MySQL 正则表达式通常是在检索数据库记录的时候&#xff0c;根据指定的匹配模式匹配记录中 符合要求的特殊字符串。MySQL 的…

Flowable串行流程移动活动

在Activiti6和Activiti7中跳转节点都要自己实现&#xff0c;Flowable增加了这个功能。 一&#xff1a;简介 串行流程是指简单的一条线的流程&#xff0c;流程中如果包含会签、排它网关也算串行流程。 节点移动有三种方式&#xff1a; 向前移动foreward。向后移动back。直接跳…

【Python+requests+unittest+excel】实现接口自动化测试框架

一、框架结构&#xff1a; 工程目录 二、Case文件设计 三、基础包 base3.1 封装get/post请求&#xff08;runmethon.py&#xff09; 1 import requests2 import json3 class RunMethod:4 def post_main(self,url,data,headerNone):5 res None6 if header …

微服务nacos实战入门

注册中心 在微服务架构中&#xff0c;注册中心是最核心的基础服务之一 主要涉及到三大角色&#xff1a; 服务提供者 ---生产者 服务消费者 服务发现与注册 它们之间的关系大致如下&#xff1a; 1.各个微服务在启动时&#xff0c;将自己的网络地址等信息注册到注册中心&#x…

【科研新手指南4】ChatGPT的prompt技巧 心得

ChatGPT的prompt心得 写在最前面chatgpt咒语1&#xff08;感觉最好用的竟然是这个&#xff0c;简单方便快捷&#xff0c;不需要多轮对话&#xff09;chatgpt思维链2&#xff08;复杂任务更适用&#xff0c;简单任务把他弄复杂了&#xff09;机理chatgpt完整咒语1&#xff08;感…

Vue基础必备掌握知识点-Vue的指令系统讲解(二)

Vue指令系统继续讲解 v-for 作用:基于数据进行循环&#xff0c;多次渲染整个元素 数据类型:数组.对象.数字。。。 遍历数组语法&#xff1a;v-for"(item,index)" in 数组 item:表示每一项 index:则是表现下标 注意:v-for中的key值&#xff0c;key属性唯一的…

探秘亚马逊云科技海外服务器 | 解析跨境云计算的前沿技术与应用

目录 一、什么是海外服务器 二、不同主流海外云服务器对比 三、海外服务器的创建(亚马逊为例) 四、个人总结 一、什么是海外服务器 亚马逊云科技海外服务器&#xff1a;指的是部署在世界各地的亚马逊数据中心中的服务器设备。这些服务器提供了计算、存储、数据库、网络等各…

μC/OS-II---信号量管理1(os_sem.c)

目录 信号量管理信号量创建信号量删除获取/等待信号量发出信号量 信号量管理 信号量创建 OS_EVENT *OSSemCreate (INT16U cnt) {OS_EVENT *pevent; #if OS_CRITICAL_METHOD 3u /* Allocate storage for CPU status register */OS_CPU_SR cp…

智能穿戴AR眼镜主板方案定制_MTK平台AR智能眼镜PCB板开发

AR智能眼镜&#xff0c;是采用了多种技术实现增强现实效果&#xff0c;是将虚拟信息和现实场景相结合的智能设备。 AR智能眼镜硬件上&#xff0c;包括多个传感器、显示装置和处理器等。其中&#xff0c;传感器用于捕捉用户的动作和环境信息&#xff0c;如摄像头、陀螺仪、加速…

Hadoop入门——数据分析基本步骤

文章目录 1.概述2.分析步骤2.1第一步 明确分析目的和思路2.2第二步 数据收集2.3第三步 数据处理2.4第四步 数据分析2.5第五步 数据展现2.6第六步 报告撰写 3.总结 1.概述 2.分析步骤 2.1第一步 明确分析目的和思路 2.2第二步 数据收集 2.3第三步 数据处理 2.4第四步 数据分析 …

BEVFormer 论文阅读

论文链接 BEVFormer BEVFormer&#xff0c;这是一个将Transformer和时间结构应用于自动驾驶的范式&#xff0c;用于从多相机输入中生成鸟瞰&#xff08;BEV&#xff09;特征利用查询来查找空间/时间&#xff0c;并相应地聚合时空信息&#xff0c;从而为感知任务提供更强的表示…