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

news2025/1/11 18:40:19

       前言 

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


       目录

       单例模式

        饿汉模式:       

        懒汉模式:

       什么是阻塞队列

        什么是高内聚 低耦合

       阻塞队列的实现


       单例模式

        单例模式(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.coloradmin.cn/o/1198774.html

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

相关文章

vue,react虚拟dom

Virtual DOM 前言 在传统的Web开发中,直接操作真实的DOM通常是一个昂贵且低效的操作。为了解决这个问题,Virtual DOM(虚拟DOM)被引入为一个中间层,允许开发者在内存中进行操作,从而避免频繁且不必要的真实D…

【LeetCode刷题-二分查找】--69.x的平方根

69.x的平方根 class Solution {public int mySqrt(int x) {int l 0,r x ,ans -1;while(l < r){int mid (r - l)/2 l;if((long)mid * mid <x){ans mid;l mid 1;}else{r mid - 1;}}return ans;} }

(SpringBoot)第五章:SpringBoot创建和使用

文章目录 一&#xff1a;Spring和SpringBoot&#xff08;1&#xff09;Spring已解决和未解决的问题&#xff08;2&#xff09;SpringBoot 二&#xff1a;Spring项目的创建&#xff08;1&#xff09;IDEA创建&#xff08;2&#xff09;网页端创建 三&#xff1a;项目目录介绍及运…

在CMake中打印日志信息

message([STATUS|WARNING|AUTHOR_WARNING|FATAL_ERROR|SEND_ERROR] "message to display" ...) (无) &#xff1a;重要消息 STATUS &#xff1a;非重要消息 WARNING&#xff1a;CMake 警告, 会继续执行 AUTHOR_WARNING&#xff1a;CMake 警告 (dev), 会继续执行 SEN…

【左程云算法全讲7】二叉树基础

系列综述&#xff1a; &#x1f49e;目的&#xff1a;本系列是个人整理为了秋招面试的&#xff0c;整理期间苛求每个知识点&#xff0c;平衡理解简易度与深入程度。 &#x1f970;来源&#xff1a;材料主要源于左程云算法课程进行的&#xff0c;每个知识点的修正和深入主要参考…

Python之函数进阶-闭包原理

Python之函数进阶-闭包原理 闭包 自由变量&#xff1a;未在本地作用域中定义的变量&#xff0c;例如定义在内层函数外的外层函数的作用域中的变量闭包&#xff1a;就是一个概念&#xff0c;出现在嵌套函数中&#xff0c;指的是内层函数引用到了外层函数的自由变量&#xff0c…

降维·预测·救命:PCA、随机森林与乳腺癌

一、引言 乳腺癌作为女性健康领域的一大挑战&#xff0c;对全球范围内的女性健康产生了深远影响。据世界卫生组织&#xff08;WHO&#xff09;统计&#xff0c;乳腺癌已成为全球女性恶性肿瘤发病率的最高者&#xff0c;且呈现逐年上升的趋势。在中国&#xff0c;乳腺癌也是女性…

【LeetCode刷题笔记】滑动窗口

992. K 个不同整数的子数组 解题思路: 滑动窗口 , 题目问题转化为: 求 「最多存在 K 个不同整数的子数组的个数」 与 「最多存在 K - 1 个不同整数的子数组的个数」 之差, 就是题目所求的 「恰好存在 K 个不同整数的子数组的个数」 , 最终问题就变成求解滑动窗口内,以 R …

Mac媒体播放器 Fig Player 1.3.7

Fig Player是一款功能强大的多媒体播放器软件&#xff0c;适用于macOS操作系统。它支持广泛的音频和视频格式&#xff0c;如MP3、MP4、AVI、MKV等&#xff0c;并提供了直观易用的用户界面&#xff0c;使用户可以轻松浏览和管理媒体库。 Fig Player具有快速加载和播放媒体文件的…

在 Azure 上构建和部署自然语言处理模型

自然语言处理&#xff08;Natural Language Processing&#xff0c;NLP&#xff09;是人工智能领域中的重要分支之一&#xff0c;它涉及对人类语言进行理解和分析。在 Azure 平台上&#xff0c;我们可以利用丰富的工具和服务来构建和部署自然语言处理模型&#xff0c;实现从文本…

PTA_乙级_1086

#include <iostream> #include<string> #include<algorithm> using namespace std;int reverseNum(int n){//将数字n转为字符串string nStrto_string(n);//翻转字符串reverse(nStr.begin(),nStr.end());//转为数字int resstoi(nStr);return res; }int main()…

六:ffmpe音频参数的使用

-aframes 设置要输出的音频帧数 -b:a 设置音频码率 -ar 设定采样率 -ac 设定声音的Channel数 -acodec 设定声音的编解码器&#xff0c;如果用copy标识原始编解码数据必须被copy -an 不处理音频 -af 音频过滤器 写笔记前查阅了其它说明&#xff0c; -aframes&#xff1a;等价…

基于JavaWeb+SSM+Vue校内校园二手交易微信小程序系统的设计和实现

基于JavaWebSSMVue校内校园二手交易微信小程序系统的设计和实现 源码传送入口前言主要技术系统设计功能截图Lun文目录订阅经典源码专栏Java项目精品实战案例《500套》 源码获取 源码传送入口 前言 在Internet高速发展的今天&#xff0c;我们生活的各个领域都涉及到计算机的应…

【算法|二分查找No.6】leetcode 153. 寻找旋转排序数组中的最小值

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【手撕算法系列专栏】【LeetCode】 &#x1f354;本专栏旨在提高自己算法能力的同时&#xff0c;记录一下自己的学习过程&#xff0c;希望…

回收站清空了怎么恢复?数据恢复的 6 种方法

众所周知&#xff0c;计算机中的回收站是一个存储空间&#xff0c;用于存储从计算机系统中删除的所有文件、文件夹或数据。它是大多数计算机系统&#xff08;包括Windows、Mac等&#xff09;上的必备功能。当从计算机中删除文件或文件夹时&#xff0c;它会在回收站中存储指定的…

MySQL最新2023年面试题及答案,汇总版(5)【MySQL最新2023年面试题及答案,汇总版-第三十五刊】

文章目录 MySQL最新2023年面试题及答案&#xff0c;汇总版(5)01、对MySQL的锁了解吗&#xff1f;02、MySQL中有哪几种锁&#xff1f;03、如何删除索引&#xff1f;04、索引能干什么?05、MySql, Oracle&#xff0c;Sql Service的区别&#xff1f;06、varchar与char的区别&#…

体验前所未有的显示器管理体验:BetterDisplay Pro Mac

在现代的数字化时代&#xff0c;显示器是我们日常生活和工作中不可或缺的一部分。从笔记本电脑到台式机&#xff0c;从平板电脑到手机&#xff0c;几乎所有的电子设备都配备了显示器。然而&#xff0c;对于专业人士和从事设计行业的人来说&#xff0c;仅仅依靠系统自带的显示器…

网网络安全基础之php开发 文件读取、写入功能的实现

前言 续之前的系列&#xff0c;这里php开发的文件操作的内容读取以及文本写入的部分 文件读取代码的实现 css代码 本系列的php博客都是这个css&#xff0c;名字都是index.css /* css样式初始化 */ * {font-family: Poppins, sans-serif;margin: 0;padding: 0;box-sizing: …

Python读取csv文件并绘制曲线

前言 有时候我们的数据保存在csv文件中&#xff0c;但是想要更加直观的看出数据的好坏&#xff0c;最好利用matplotlib来画出曲线图 数据准备 我的数据格式如下&#xff1a; 在画图时&#xff0c;我需要把第一行去掉 # 去除第一个元素 xdata xdata.drop(xdata.index[0])…

简单描述下微信小程序的相关文件类型以及微信小程序和uniapp的区别?

前言 微信小程序是一种基于微信平台的轻量级应用&#xff0c;其文件类型涵盖了不同用途的文件。了解这些文件类型能帮助开发者更好地构建小程序。 微信小程序是微信平台上的一种轻量级应用程序&#xff0c;为用户提供了一种便捷的方式来在微信生态系统内进行各种操作和获取服务…