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

Java多线程——同步

同步是什么?

当两个线程同时对一个变量进行修改时,不同的访问顺序会造成不一样的结果,这时候就需要同步保证结果的唯一性。

未同步时

新建Bank类,transfer()用于在两个账户之间转账金额

class Bank {private double[] accounts;public Bank(int accountNum, double initialMoney) {accounts = new double[accountNum];Arrays.fill(accounts, initialMoney);}public void transfer(int from, int to, double money) {if (accounts[from] < money) {return;}System.out.print(Thread.currentThread());accounts[from] -= money;System.out.printf("%10.2f from %d to %d", money, from, to);accounts[to] += money;System.out.printf(" Total %10.2f%n", getTotal());}public double getTotal() {double total = 0.0d;for (double temp : accounts) {total += temp;}return total;}public int size() {return accounts.length;}
}

假设银行有1000个开户人,每个人账户有1000元,新建1000个线程进行随机转账,无论怎么转账,总金额都应该为1000000,但实际的钱却越来越少(这个例子不太好,原因是浮点数加减可能误差)

int NACCOUNTS = 1000;
double INITIAL_BALANCE = 1000;
double MAX_AMOUNT = 1000;
int DELAY = 10;Bank bank = new Bank(NACCOUNTS, INITIAL_BALANCE);
for (int i = 0; i < NACCOUNTS; i++) {int fromAccount = i;new Thread(new Runnable() {@Overridepublic void run() {try {while (true) {int toAccount = (int) (bank.size() * Math.random());double money = MAX_AMOUNT * Math.random();bank.transfer(fromAccount, toAccount, money);Thread.sleep((int) (DELAY * Math.random()));}} catch (InterruptedException e) {e.printStackTrace();}}}).start();
}

原因分析:

  • 当两个线程同时执行到accounts[to] += money;
  • 线程1取出accounts[to](如900)放到寄存器,+money(如100),正准备写回accounts[to](变成了1000)时
  • 线程2抢占到权限执行accounts[to] += money 将accounts[to]修改为了10002
  • 线程1再写就将10002覆盖成了1000

ReentrantLock

修改Bank,对其transfer方法加锁,注意lock应该为成员变量,即每个Bank实例都只有一把锁,不同对象之间的锁不会互相影响,需要在finally释放锁

class Bank {private double[] accounts;private ReentrantLock lock = new ReentrantLock();public Bank(int accountNum, double initialMoney) {accounts = new double[accountNum];Arrays.fill(accounts, initialMoney);}public void transfer(int from, int to, double money) {if (accounts[from] < money) {return;}lock.lock();try {System.out.print(Thread.currentThread());accounts[from] -= money;System.out.printf("%10.2f from %d to %d", money, from, to);accounts[to] += money;System.out.printf(" Total %10.2f%n", getTotal());} finally {lock.unlock();}}public double getTotal() {double total = 0.0d;for (double temp : accounts) {total += temp;}return total;}public int size() {return accounts.length;}
}

Condition

在转账时,应该避免选择没有足够资金的账号转出

if(bank.getMoney(fromAccount) >= money){bank.transfer(fromAccount, toAccount, money);
}

但在多线程情况下,可能两个线程同时进入if,一个线程转账后导致另外一个线程金额不够转账,故我们要确保在检查之后加锁禁止别的线程先行一步

private ReentrantLock lock = new ReentrantLock();
lock.lock();
try {while(account[from]  < money){}
}finally{lock.unlock();
}

但当账户没有钱的时候,转出线程会一直等待其他线程转入资金,而其他线程因为无法拿到锁而无法转入,这就造成了死锁,这时候就需要条件对象,当金额不足时阻塞线程放弃锁的持有

private ReentrantLock lock = new ReentrantLock();
private Condition moneyEnough;lock.lock();
try {moneyEnough = lock.newCondition();while(account[from]  < money){moneyEnough.await();}
}finally{lock.unlock();
}

当其他线程转账后,应该调用signalAll()唤起所有await()中的线程,当其中的某个线程被调度并再次获取锁后,会再进入try子句检测金额是否足够

moneyEnough.signalAll();

Tips:

  • 还有一个signal()方法随机唤起一个等待线程
  • 当所有线程的金额都小于转账金额,调用await(),所有线程都会阻塞,此时会再次死锁

synchronized

Java中每一个对象都有一个内部锁,如果一个方法用synchronized声明,线程调用该方法时需要获得其内部锁,即

public synchronized void method(){}

等价于

private ReentrantLock innerLock = new ReentrantLock();
public void method(){innerLock.lock();try{}finally{innerLock.unlock();}
}

内部锁只有一个条件,对其的阻塞和唤醒调用wait()、notifyAll()/notify(),它们是Object中的final方法,相当对ReentrantLock调用

innerLock.await();
innerLock.signalAll();

将静态方法声明为synchronized,调用该方法时会锁住对应的类,此时其他线程无法调用该类的其他同步静态方法

Tips:

  • 内部锁不能中断一个正在试图获得锁的线程
  • 锁时不能设置超时
  • 只有一个条件

该使用哪种锁机制?

使用synchronized可减少代码的编写,减少出错的几率

使用ReentrantLock+Condition可以自行控制锁的过程,实现多个条件

同步阻塞

使用其他对象的锁来完成原子操作

public class bank{private Object lock = new Object();public void transfer(int from, int to, double money) {synchronized(lock){}}
}

监视器

volatile

若如果只有一两个域可能发生多线程的误写,可对该域声明为volatile,虚拟机和编译器就知道该域是可能被另一个线程并发更新的,但其不能保证原子性

boolean flag;
public void Not(){flag = !flag;
}

如上,不能保证其再读取、翻转和写入时不被中断

final和锁

当把域声明为final时,其他线程对其的读取只能是构造成功后的值,而不会是null

fial Map<String, Double> accounts = new HashMap<>();

原子性

死锁

线程局部变量

当多个线程都要调用Random中的方法生成随机数时,由于Random是加锁的,其他线程就得等待,此时可用TheadLocal辅助类为各个线程提供各自的Random实例

ThreadLocal<Random> threadLocal = ThreadLocal.withInitial(() -> new Random());
threadLocal.get().nextInt();

此外,专门创建多线程随机数的ThreadLocalRandom,其current()方法会返回当前线程的Random类实例

ThreadLocalRandom.current().nextInt();

锁超时

当线程调用tryLock()方法去申请另一个线程的锁时,很有可能发生阻塞,故可在申请时设置时长

private ReentrantLock lock = new ReentrantLock();
try {lock.tryLock(100, TimeUnit.SECONDS);
} catch (InterruptedException e) {e.printStackTrace();
}

读写锁

读写锁可从ReentrantReadWriteLock取出,为所有获取方法加上读锁,为所有修改方法加上写锁

ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
Lock readLock = reentrantReadWriteLock.readLock();
Lock writeLock = reentrantReadWriteLock.writeLock();

为什么弃用stop()和suspend()

stop()方法用来终止线程,并立即释放线程所获得的锁,这会导致对象状态不一致(如钱已被转出,但在转入前stop,会导致数据丢失)

故无法确定什么时候调用stop()是安全的,在希望停止线程的时候应该中断线程,被中断的线程会在安全的时候停止

suspend()方法用来阻塞线程,直至另一个线程调用resume(),当用suspend()挂起一个持有一个锁的线程,则该锁在resume()之前是不可用的。

若此时再用suspend()方法的线程获取该锁,则会死锁,被挂起的锁等待resume()释放,而要resume()则要获取被挂起的锁

相关文章:

Java多线程——同步

同步是什么&#xff1f; 当两个线程同时对一个变量进行修改时&#xff0c;不同的访问顺序会造成不一样的结果&#xff0c;这时候就需要同步保证结果的唯一性。 未同步时 新建Bank类&#xff0c;transfer()用于在两个账户之间转账金额 class Bank {private double[] account…...

Vue+NodeJS实现邮件发送

一.邮箱配置 这里以QQ邮箱为例,网易邮箱类似. 设置->账号 二.后端服务搭建 index.js const express require(express) const router require(./router); const app express()// 使用路由文件 app.use(/,router);app.listen(3000, () > {console.log(server…...

Go语言网络编程(socket编程)TCP粘包

1、TCP粘包 服务端代码如下&#xff1a; // socket_stick/server/main.gofunc process(conn net.Conn) {defer conn.Close()reader : bufio.NewReader(conn)var buf [1024]bytefor {n, err : reader.Read(buf[:])if err io.EOF {break}if err ! nil {fmt.Println("read…...

【再识C进阶2(中)】详细介绍指针的进阶——函数指针数组、回调函数、qsort函数

前言 &#x1f493;作者简介&#xff1a; 加油&#xff0c;旭杏&#xff0c;目前大二&#xff0c;正在学习C&#xff0c;数据结构等&#x1f440; &#x1f493;作者主页&#xff1a;加油&#xff0c;旭杏的主页&#x1f440; ⏩本文收录在&#xff1a;再识C进阶的专栏&#x1…...

PaddleOCR学习笔记3-通用识别服务

今天优化了下之前的初步识别服务的python代码和html代码。 采用flask paddleocr bootstrap快速搭建OCR识别服务。 代码结构如下&#xff1a; 模板页面代码文件如下&#xff1a; upload.html : <!DOCTYPE html> <html> <meta charset"utf-8"> …...

9.8 校招 实习 内推 面经

绿泡*泡&#xff1a; neituijunsir 交流裙 &#xff0c;内推/实习/校招汇总表格 1、校招 | 长安福特2024校园招聘正式启动 校招 | 长安福特2024校园招聘正式启动 2、2023校招总结--SLAM岗位 - 5 2023校招总结--SLAM岗位 - 5 3、校招&实习 | 格灵深瞳2024秋季校园招聘启…...

web前段与后端的区别优漫动游

要了解web前后端的区别&#xff0c;首先必须得清楚什么是web前端和web后端。 web前段与后端的区别 首先&#xff1a;web的本意是蜘蛛网和网的意思&#xff0c;在网页设计中我们称为网页的意思。现广泛译作网络、互联网等技术领域。表现为三种形式&#xff0c;即超文本(hyp…...

局域网ntp服务器设置(windows时间同步服务器NetTime)(ubuntu systemd-timesyncd ntp客户端)123端口、ntp校时

文章目录 背景windows如何配置ntp服务器手动配置配置参数AnnounceFlags和Enabled含义 使用软件配置&#xff08;NetTime&#xff09;实操相关疑问&#xff1a;0.nettime.pool.ntp.org是什么&#xff1f; 注意事项请务必检查windows主机123端口是否已被占用&#xff0c;方法请参…...

【个人博客系统网站】我的博客列表页 · 增删改我的博文 · 退出登录 · 博客详情页 · 多线程应用

【JavaEE】进阶 个人博客系统&#xff08;4&#xff09; 文章目录 【JavaEE】进阶 个人博客系统&#xff08;4&#xff09;1. 增加博文1.1 预期效果1.1 约定前后端交互接口1.2 后端代码1.3 前端代码1.4 测试 2. 我的博客列表页2.1 期待效果2.2 显示用户信息以及博客信息2.2.1…...

安全狗陈奋:数据安全需要建立在传统网络安全基础之上

8月22日-23日&#xff0c;由创业邦主办的“2023 DEMO WORLD 企业开放式创新大会”在上海顺利举行。 作为国内云原生安全领导厂商&#xff0c;安全狗受邀出席此次活动。 本次大会以“拥抱开放”为主题&#xff0c;聚焦开放式创新&#xff0c;通过演讲分享、专场对接、需求发布…...

【Redis】深入探索 Redis 的数据类型 —— 哈希表 hash

文章目录 前言一、hash 类型相关命令1.1 HSET 和 HSETNX1.2 HGET 和 HMGET1.3 HKEYS、HVALS 和 HGETALL1.4 HEXISTS 和 HDEL1.5 HLEN1.6 HINCRBY 和 HINCRBYFLOAT1.7 哈希相关命令总结 二、hash 类型内部编码三、hash 类型的应用场景四、原生&#xff0c;序列化&#xff0c;哈希…...

网络安全应急响应典型案例-(DDOS类、僵尸网络类、数据泄露类)

一、DDOS类事件典型案例 DDOS攻击&#xff0c;即分布式拒绝服务攻击&#xff0c;其目的在于使目标电脑的网络或系统资源耗尽&#xff0c;使服务暂时中断或停止&#xff0c;导致其正常用户无法访问。CC攻击使用代理服务器向受害服务器发送大量貌似合法的请求&#xff08;通常…...

【测试开发】Mq消息重复如何测试?

本篇文章主要讲述重复消费的原因&#xff0c;以及如何去测试这个场景&#xff0c;最后也会告诉大家&#xff0c;目前互联网项目关于如何避免重复消费的解决方案。 Mq为什么会有重复消费的问题? Mq 常见的缺点之一就是消息重复消费问题&#xff0c;产生这种问题的原因是什么呢…...

C++和C#程序语言的区别

一直学习C++和C#,两者之间的区别总结一下 目录 一、两种语言概述 C++语言 C#语言 二、两种语言对比 2.1运行依赖...

CentOS配置Java环境报错-bash: /usr/local/jdk1.8.0_381/bin/java: 无法执行二进制文件

CentOS配置Java环境后执行java -version时报错&#xff1a; -bash: /usr/local/jdk1.8.0_381/bin/java: 无法执行二进制文件原因是所使用的jdk的版本和Linux内核架构匹配不上 使用以下命令查看Linux架构&#xff1a; [rootlocalhost ~]# cat /proc/version Linux version 3.1…...

MySQL进阶 —— 超详细操作演示!!!(上)

MySQL进阶 —— 超详细操作演示&#xff01;&#xff01;&#xff01;&#xff08;上&#xff09; 一、存储引擎1.1 MySQL 体系结构1.2 存储引擎介绍1.3 存储引擎特点1.4 存储引擎选择 二、索引2.1 索引概述2.2 索引结构2.3 索引分类2.4 索引语法2.5 SQL 性能分析2.6 索引使用2…...

一条爬虫抓取一个小网站所有数据

一条爬虫抓取一个小网站所有数据 ​ 今天闲来无事&#xff0c;写一个爬虫来玩玩。在网上冲浪的时候发现了一个搞笑的段子网&#xff0c;发现里面的内容还是比较有意思的&#xff0c;于是心血来潮&#xff0c;就想着能不能写一个Python程序&#xff0c;抓取几条数据下来看看&am…...

八大排序——快速排序

Hello&#xff0c;大家好&#xff0c;今天分享的八大排序里的快速排序&#xff0c;所谓快速排序是一个叫霍尔的人发明&#xff0c;有很多人可能会觉得为什么不叫霍尔排序&#xff0c;其中原因就是因为它快&#xff0c;快速则体现了它的特点&#xff0c;今天我们就来讲一下快速排…...

【ES】笔记-Class类剖析

Class Class介绍与初体验ES5 通过构造函数实例化对象ES6 通过Class中的constructor实列化对象 Class 静态成员实例对象与函数对象的属性不相通实例对象与函数对象原型上的属性是相通的Class中对于static 标注的对象和方法不属于实列对象&#xff0c;属于类。 ES5构造函数继承Cl…...

数学建模--Seaborn库绘图基础的Python实现

目录 1.绘图数据导入 2. sns.scatterplot绘制散点图 3.sns.barplot绘制条形图 4.sns.lineplot绘制线性图 5.sns.heatmap绘制热力图 6.sns.distplot绘制直方图 7.sns.pairplot绘制散图 8.sns.catplot绘制直方图 9.sns.countplot绘制直方图 10.sns.lmplot绘回归图 1.绘图数…...

golang循环变量捕获问题​​

在 Go 语言中&#xff0c;当在循环中启动协程&#xff08;goroutine&#xff09;时&#xff0c;如果在协程闭包中直接引用循环变量&#xff0c;可能会遇到一个常见的陷阱 - ​​循环变量捕获问题​​。让我详细解释一下&#xff1a; 问题背景 看这个代码片段&#xff1a; fo…...

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

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

什么是库存周转?如何用进销存系统提高库存周转率?

你可能听说过这样一句话&#xff1a; “利润不是赚出来的&#xff0c;是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业&#xff0c;很多企业看着销售不错&#xff0c;账上却没钱、利润也不见了&#xff0c;一翻库存才发现&#xff1a; 一堆卖不动的旧货…...

【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表

1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...

基于matlab策略迭代和值迭代法的动态规划

经典的基于策略迭代和值迭代法的动态规划matlab代码&#xff0c;实现机器人的最优运输 Dynamic-Programming-master/Environment.pdf , 104724 Dynamic-Programming-master/README.md , 506 Dynamic-Programming-master/generalizedPolicyIteration.m , 1970 Dynamic-Programm…...

算法岗面试经验分享-大模型篇

文章目录 A 基础语言模型A.1 TransformerA.2 Bert B 大语言模型结构B.1 GPTB.2 LLamaB.3 ChatGLMB.4 Qwen C 大语言模型微调C.1 Fine-tuningC.2 Adapter-tuningC.3 Prefix-tuningC.4 P-tuningC.5 LoRA A 基础语言模型 A.1 Transformer &#xff08;1&#xff09;资源 论文&a…...

人机融合智能 | “人智交互”跨学科新领域

本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...

Axure 下拉框联动

实现选省、选完省之后选对应省份下的市区...

CMS内容管理系统的设计与实现:多站点模式的实现

在一套内容管理系统中&#xff0c;其实有很多站点&#xff0c;比如企业门户网站&#xff0c;产品手册&#xff0c;知识帮助手册等&#xff0c;因此会需要多个站点&#xff0c;甚至PC、mobile、ipad各有一个站点。 每个站点关联的有站点所在目录及所属的域名。 一、站点表设计…...

使用 uv 工具快速部署并管理 vLLM 推理环境

uv&#xff1a;现代 Python 项目管理的高效助手 uv&#xff1a;Rust 驱动的 Python 包管理新时代 在部署大语言模型&#xff08;LLM&#xff09;推理服务时&#xff0c;vLLM 是一个备受关注的方案&#xff0c;具备高吞吐、低延迟和对 OpenAI API 的良好兼容性。为了提高部署效…...