当前位置: 首页 > news >正文

JavaEE-多线程编程单例模式

一、等待通知

系统内部,线程之间是抢占式执行的,随即调度,程序可以通过手动干预的方式,能够让线程一定程度的按咱们想要的顺序执行,无法主动让某个线程被调度,但可以主动让某个线程等待。等待通知可以安排线程之间的执行顺序。

举个栗子:当t1线程要在队列获取元素,由于此时队列是空的无法进行工作,它只能频繁的进行获取释放锁的操作,导致其他线程不能得到cpu分配资源,线程中调度是无序的,这种情况很可能出现,称为——线程饿死(不会像死锁那样卡死,但是可能会卡一下,影响程序效率)

等待通知机制可以解决上述问题条件判断是否能执行当前逻辑,不能就主动wait阻塞等待,把执行的机会让给别的线程,避免该线程进行一些无意义的重试,等时机成熟时(其他线程通知-notify),阻塞被唤醒。代码实现

public static void main(String[] args) {Object locker = new Object();Thread t1 = new Thread(()->{synchronized (locker){System.out.println("t1等待前");try {locker.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.println("t1等待后");});

当我们执行这样的逻辑时,线程就会在执行完第一句输出语句后通过wait阻塞等待,注意因为wait操作被执行时是先解锁然后阻塞等待,解锁的前提是有锁,所以需要在操作前先加锁。此时可以通过jconsole来查看线程状态:

可以看出此时是WAITING状态。再写另一个线程来唤醒它:

Thread t2 = new Thread(()->{try {Thread.sleep(500);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (locker){System.out.println("t2唤醒前");locker.notify();System.out.println("t2唤醒后");}});

此时工作台可以从输出顺序看到执行过程

(1)t1线程获取锁 (2)t1线程阻塞等待且解锁 (3)t2线程获取锁 (4)t2线程唤醒t1线程,执行完逻辑后释放锁 (5)t1重新获取锁,从上次被阻塞的地方继续执行。t1的状态变化是:WAITING->RUNNABLE->BLOCKED 处于blocked状态是因为唤醒后需要等t1先释放锁。

注意:notify一次只能唤醒一个线程,而且是随机的。不过notify也有可以唤醒所有线程的方法:

locker.notifyAll();

wait也有一个带参数的版本,无参数版本采用的是死等战术,等不到唤醒程序就一直等,带参数版本和join差不多,过了参数时间就不会再阻塞状态。

二、单例模式

单例模式是一种经典的设计模式,相比其他的设计模式算是比较简单的设计模式,也是面试中常考的设计模式。

单例模式->单个实例,整个进程中有且只有一个对象,这样的对象就成为单例(instance),那么如何保证进程中只有一个实例呢?

需要让编译器帮我们进行检查,通过编码上的技巧,使编译器自动发现我们是否创建了多个实例,并尝试创建多个实例时,直接编译报错。

单例模式有很多种写法,本篇文章主要介绍两种:饿汉模式&懒汉模式

1、饿汉模式

先看这样的一串代码:

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

static成员初始化时机是在类加载的时候,可以简单理解为JVM一启动就立即加载,成员也就立即创建了。static修饰的类属性是类对象的,每个类的类对象在JVM中只有一个,里面的静态成员只有一个,初始化也只执行一次,当后续需要这个类的实例时可以通过方法来获取已经创建好的实例,而不是再创建新的,这个方法为:

 public static Singleton getInstance() {return instance;}

那么如果其他线程想通过此类创建新的对象该怎么办呢?

当类之外的代码想尝试创建新的对象时一定会调用构造方法,所以将构造方法的权限设置为private时就会无法调用,编译报错。如下:

private Singleton(){/  }

当类一加载静态成员就被创建了,就像饿的人看见吃的会想赶紧吃的感觉一样,所以这种模式可以被称为“饿汉模式”。

2、懒汉模式

在计算机中,懒往往是一个褒义词,代表着高效率。相对于饿汉模式一加载类就创建对象,懒汉则是当第一次需要使用对象才会去创建,就把创建实例的代价省下来了,按照这个思路来创建类:

class SingletonLazy{public static SingleLazy instance = null;public static SingleLazy getInstance() {if(instance == null){instance = new SingleLazy();}return instance;}
}

先将静态成员的引用指向空,当需要创建实例时判断当前引用是否为空,为空时再创建新的,不为空就直接返回实例。懒的本质就是偷懒,能少做就少做,懒->缓。

如果代码中存在多个单例类,都使用懒汉模式的话这些实例会在程序启动时扎堆的创建,可能把程序启动时间拖慢,如果使用饿汉模式的话,调用时机是分散的,化整为0,让用户感受不到卡顿

多线程模式下分析懒汉模式与饿汉模式

思考当多个线程同时getInstance时这两种模式是否会引起线程不安全问题?

饿汉模式安全,但懒汉模式是不安全的。

饿汉模式安全的原因:创建实例的时机是java进程启动时,比主线程还早创建,因此在其他线程调用getInstance时实例肯定已经创建好了,每个线程只做了一件事,就是读取上述静态变量的值,多个线程读取一个变量,安全。

而懒汉模式与其不同,懒汉模式的关键操作代码是这些

第一行是:“读”,查看一下实例引用的地址的是否为空,而第二行是赋值,也就是修改操作,上述操作在多线程环境下容易出现问题,比如会产生下面这种执行顺序

假定最初instance引用为空,t1判断引用为空,t2判断引用为空,t1创建实例对象,由于t2已经判定完是否为空,所以也会创建实例对象。

上述代码t2创建的引用会覆盖掉t1的引用的地址,进一步t1的instance没有指向了就会被GC回收掉

解决办法可以通过加锁的方式来保证懒汉模式下getInstance是安全的,当t1线程进入判定语句时t2需阻塞等待,t1创建完实例释放锁后t2才能获取锁,开始判定操作,此时的instance就已经指向了地址不为空了。初步优化后的代码:

class SingletonLazy{public static SingletonLazy instance = null;public static SingletonLazy getInstance() {synchronized (SingletonLazy.class){if(instance == null){instance = new SingletonLazy();}}return instance;}
}

但是懒汉模式只有在初次调用getInstance时会涉及到线程安全问题,一旦实例创建好了后面再调用都是只读操作,不涉及线程安全问题,而后续调用明明没有线程安全问题还要加锁,增加了没必要的开销

解决办法在加锁前再判断一次当前调用是否为第一次调用,如果是第一次调用再去获取锁,判定条件还是看instance是否为空即可。

别忘了上篇文章提到的volatile,二次优化后的代码:

class SingletonLazy{public static volatile SingletonLazy instance = null;public static SingletonLazy getInstance() {if(instance == null){synchronized (SingletonLazy.class){if(instance == null){instance = new SingletonLazy();}}}return instance;}
}

通过双重if避免了重复创建对象。

下篇文章更新多线程编程经典案例二——阻塞队列

感谢观看

道阻且长,行则将至

相关文章:

JavaEE-多线程编程单例模式

一、等待通知 系统内部,线程之间是抢占式执行的,随即调度,程序可以通过手动干预的方式,能够让线程一定程度的按咱们想要的顺序执行,无法主动让某个线程被调度,但可以主动让某个线程等待。等待通知可以安排…...

RHCA III之路---EX436-6

RHCA III之路---EX436-6 1. 题目2. 解题3. 确认 1. 题目 2. 解题 三台node分别运行 yum install -y device-mapper-multipath mpathconf --enable systemctl enable --now multipathd3. 确认 fdisk -l...

Vuex模块化 深入浅出超详细

Vuex 模块化 为什么需要模块化? 随着项目规模的增长,单一的 store 文件会变得庞大且难以管理; Vuex 的模块化是一种组织和管理应用状态的策略:,它允许将全局的状态管理分解成更小、更可管理的部分; 逻辑清…...

细说MCU检测按键输入的外部中断和修改HAL_GPIO_EXTI_IRQHandler() 的实现方法

目录 一、 硬件板及设计目的 二、建立工程 1.配置GPIO 2.配置时钟源和Debug 3.配置系统时钟 4.配置NVIC 三、代码编写 四、修改HAL_GPIO_EXTI_IRQHandler() 一、 硬件板及设计目的 本文使用的硬件板是ST的开发板NUCLEO-G474RE,板上MCU型号为ST…...

昂科烧录器支持XHSC小华半导体的32位微控制器HC32F005C6P

芯片烧录行业领导者-昂科技术近日发布最新的烧录软件更新及新增支持的芯片型号列表,其中XHSC小华半导体的32位微控制器HC32F005C6P已经被昂科的通用烧录平台AP8000所支持。 HC32F005C6P是Low Pin Count、宽电压工作范围的MCU,集成12位1Msps高精度SARADC…...

根据 IP 地址配置子网示例(下挂 hub 接不同 vlan 终端)

我们一般根据端口配置子网比较简单,但是如果换接口,就又要到交换机上重新配置端口所属 vlan 了,紧急情况下,还是比较耽误时间的。但如果根据IP地址配置 vlan,则可以插在交换机上任意端口,排障时比较节省时间…...

Flink-DataWorks第四部分:数据同步(第60天)

系列文章目录 2.4.2 DataStudio侧实时同步 2.4.3 数据集成侧同步任务 文章目录 系列文章目录前言2.4.2 DataStudio侧实时同步2.4.3 数据集成侧同步任务 前言 本文主要详解了DataWorks的数据同步,为第四部分: 由于篇幅过长,分章节进行发布。…...

go post请求,参数是raw json格式,response是固定结构。

在Go语言中,使用net/http包可以很方便地发送HTTP请求,包括POST请求。当需要发送raw JSON格式的参数时,通常会使用encoding/json包来将Go的结构体序列化为JSON字符串,然后使用http.NewRequest函数创建请求,并通过http.C…...

国产开源大模型都有哪些?

随着ChatGPT引领的大模型热潮,国内的公司开始相继投入研发自己的人工智能大模型,截止到2023年10月,国产公司的大模型有近百个,包括一些通用大模型,比如百度的文心一言,也有特定领域的专用大模型&#xff0c…...

基于Hadoop的超市进货推荐系统设计与实现【springboot案例项目】

文章目录 有需要本项目的代码或文档以及全部资源,或者部署调试可以私信博主项目介绍系统分析系统设计数据表设计表4-1:关于我们表4-2:用户表4-3:管理员表表4-4:token表表4-5:系统简介表4-6:收藏…...

ChatGPT能从这几个方面提升学术论文质量

学境思源,一键生成论文初稿: AcademicIdeas - 学境思源AI论文写作 写作和编辑高质量的学术论文是一项具有挑战性的任务。随着人工智能技术的进步,ChatGPT作为一种强大的语言生成工具,正逐渐成为提升论文质量的得力助手。从头脑风…...

Python3的安装及基础指令

Day 20 基础语法 1、环境:python2内置,安装并使用python3,最新版3.12版可以使用源码安装 # 查看python版本号 [rootpython ~]#yum list installed|grep python [rootpython ~]#yum list installed|grep epel [rootpython ~]# yum -y …...

使用Spring与JDK动态代理实现事务管理

使用Spring与JDK动态代理实现事务管理 在现代企业级应用开发中,事务管理是一项关键的技术,它可以保证一系列操作要么全部成功,要么全部失败,从而确保数据的一致性和完整性。Spring框架提供了强大的事务管理能力,但有时…...

服务器硬件及RAID配置

服务器及 RAID 磁盘阵列介绍 RAID0 俗称 “ 条带 ” ,它将两个或多个硬盘组成一个逻辑硬盘,容量是所有硬盘之和,因 为是多个硬盘组合成一个,故可并行写操作,写入速度提高,但此方式硬盘数据没有冗余&#…...

【经验总结】ShardingSphere5.2.1 + Springboot 快速开始

Sharding Sphere 官方文档地址: https://shardingsphere.apache.org/document/current/cn/overview/maven仓库:https://mvnrepository.com/artifact/org.apache.shardingsphere/shardingsphere-jdbc 官方的文档写的很详尽到位,这里会截取部分…...

基于Golang实现Kubernetes边车模式

本文介绍了如何基于 Go 语言实现 Kubernetes Sidecar 模式,并通过实际示例演示创建 Golang 实现的微服务服务、Docker 容器化以及在 Kubernetes 上的部署和管理。原文: Sidecar Pattern with Kubernetes and Go[1] 在这篇文章中,我们会介绍 Sidecar 模式…...

TCP 通信全流程分析:从连接建立到数据传输的深度探索

目录 一、TCP报头 二、三次握手 三、数据传输 四、四次挥手 本文通过一次TCP通信过程的分析来学习TCP协议 一、TCP报头 如图是一份TCP报文的报头,标准报头是20个字节,还可带有选项报头,也就是TCP报头的最小长度是20字节。以下是对报头的各…...

4、提取H264码流中nalu

H264的NALU提取 1、nalu单元 定义nalu的存储单元&#xff0c;ebsp用来存储原始的包含起始码&#xff08;annexb格式&#xff09;的原始码流&#xff0c;sodb存储去除防竞争字节后的码流&#xff0c;prefix是3或4字节 nalu_def.h // nalu_def.h #pragma once#include <cs…...

哈佛大学单细胞课程|笔记汇总 (二)

哈佛大学单细胞课程|笔记汇总 &#xff08;一&#xff09; &#xff08;二&#xff09;Single-cell RNA-seq data - raw data to count matrix 根据所用文库制备方法的不同&#xff0c;RNA序列&#xff08;也被称为reads或tag&#xff09;将从转录本&#xff08;(10X Genomic…...

java中抽象类和接口的区别

文章目录 接口和抽象类的区别一、定义的区别1、抽象类2、接口 二、使用场景的区别1、抽象类2、接口 三、使用案例1、抽象类2、接口 接口和抽象类的区别 一、定义的区别 1、抽象类 关键字&#xff1a; abstract 是模棱两可的&#xff0c;似是而非的&#xff0c;无法给出具体明…...

CTF show Web 红包题第六弹

提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框&#xff0c;很难让人不联想到SQL注入&#xff0c;但提示都说了不是SQL注入&#xff0c;所以就不往这方面想了 ​ 先查看一下网页源码&#xff0c;发现一段JavaScript代码&#xff0c;有一个关键类ctfs…...

java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别

UnsatisfiedLinkError 在对接硬件设备中&#xff0c;我们会遇到使用 java 调用 dll文件 的情况&#xff0c;此时大概率出现UnsatisfiedLinkError链接错误&#xff0c;原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用&#xff0c;结果 dll 未实现 JNI 协…...

《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》

在注意力分散、内容高度同质化的时代&#xff0c;情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现&#xff0c;消费者对内容的“有感”程度&#xff0c;正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中&#xff0…...

论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)

笔记整理&#xff1a;刘治强&#xff0c;浙江大学硕士生&#xff0c;研究方向为知识图谱表示学习&#xff0c;大语言模型 论文链接&#xff1a;http://arxiv.org/abs/2407.16127 发表会议&#xff1a;ISWC 2024 1. 动机 传统的知识图谱补全&#xff08;KGC&#xff09;模型通过…...

MySQL 8.0 OCP 英文题库解析(十三)

Oracle 为庆祝 MySQL 30 周年&#xff0c;截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始&#xff0c;将英文题库免费公布出来&#xff0c;并进行解析&#xff0c;帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...

智能仓储的未来:自动化、AI与数据分析如何重塑物流中心

当仓库学会“思考”&#xff0c;物流的终极形态正在诞生 想象这样的场景&#xff1a; 凌晨3点&#xff0c;某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径&#xff1b;AI视觉系统在0.1秒内扫描包裹信息&#xff1b;数字孪生平台正模拟次日峰值流量压力…...

深度学习水论文:mamba+图像增强

&#x1f9c0;当前视觉领域对高效长序列建模需求激增&#xff0c;对Mamba图像增强这方向的研究自然也逐渐火热。原因在于其高效长程建模&#xff0c;以及动态计算优势&#xff0c;在图像质量提升和细节恢复方面有难以替代的作用。 &#x1f9c0;因此短时间内&#xff0c;就有不…...

AI+无人机如何守护濒危物种?YOLOv8实现95%精准识别

【导读】 野生动物监测在理解和保护生态系统中发挥着至关重要的作用。然而&#xff0c;传统的野生动物观察方法往往耗时耗力、成本高昂且范围有限。无人机的出现为野生动物监测提供了有前景的替代方案&#xff0c;能够实现大范围覆盖并远程采集数据。尽管具备这些优势&#xf…...

MySQL的pymysql操作

本章是MySQL的最后一章&#xff0c;MySQL到此完结&#xff0c;下一站Hadoop&#xff01;&#xff01;&#xff01; 这章很简单&#xff0c;完整代码在最后&#xff0c;详细讲解之前python课程里面也有&#xff0c;感兴趣的可以往前找一下 一、查询操作 我们需要打开pycharm …...

嵌入式学习之系统编程(九)OSI模型、TCP/IP模型、UDP协议网络相关编程(6.3)

目录 一、网络编程--OSI模型 二、网络编程--TCP/IP模型 三、网络接口 四、UDP网络相关编程及主要函数 ​编辑​编辑 UDP的特征 socke函数 bind函数 recvfrom函数&#xff08;接收函数&#xff09; sendto函数&#xff08;发送函数&#xff09; 五、网络编程之 UDP 用…...