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多线程——同步
同步是什么? 当两个线程同时对一个变量进行修改时,不同的访问顺序会造成不一样的结果,这时候就需要同步保证结果的唯一性。 未同步时 新建Bank类,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粘包 服务端代码如下: // 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函数
前言 💓作者简介: 加油,旭杏,目前大二,正在学习C,数据结构等👀 💓作者主页:加油,旭杏的主页👀 ⏩本文收录在:再识C进阶的专栏…...
PaddleOCR学习笔记3-通用识别服务
今天优化了下之前的初步识别服务的python代码和html代码。 采用flask paddleocr bootstrap快速搭建OCR识别服务。 代码结构如下: 模板页面代码文件如下: upload.html : <!DOCTYPE html> <html> <meta charset"utf-8"> …...
9.8 校招 实习 内推 面经
绿泡*泡: neituijunsir 交流裙 ,内推/实习/校招汇总表格 1、校招 | 长安福特2024校园招聘正式启动 校招 | 长安福特2024校园招聘正式启动 2、2023校招总结--SLAM岗位 - 5 2023校招总结--SLAM岗位 - 5 3、校招&实习 | 格灵深瞳2024秋季校园招聘启…...
web前段与后端的区别优漫动游
要了解web前后端的区别,首先必须得清楚什么是web前端和web后端。 web前段与后端的区别 首先:web的本意是蜘蛛网和网的意思,在网页设计中我们称为网页的意思。现广泛译作网络、互联网等技术领域。表现为三种形式,即超文本(hyp…...
局域网ntp服务器设置(windows时间同步服务器NetTime)(ubuntu systemd-timesyncd ntp客户端)123端口、ntp校时
文章目录 背景windows如何配置ntp服务器手动配置配置参数AnnounceFlags和Enabled含义 使用软件配置(NetTime)实操相关疑问:0.nettime.pool.ntp.org是什么? 注意事项请务必检查windows主机123端口是否已被占用,方法请参…...
【个人博客系统网站】我的博客列表页 · 增删改我的博文 · 退出登录 · 博客详情页 · 多线程应用
【JavaEE】进阶 个人博客系统(4) 文章目录 【JavaEE】进阶 个人博客系统(4)1. 增加博文1.1 预期效果1.1 约定前后端交互接口1.2 后端代码1.3 前端代码1.4 测试 2. 我的博客列表页2.1 期待效果2.2 显示用户信息以及博客信息2.2.1…...
安全狗陈奋:数据安全需要建立在传统网络安全基础之上
8月22日-23日,由创业邦主办的“2023 DEMO WORLD 企业开放式创新大会”在上海顺利举行。 作为国内云原生安全领导厂商,安全狗受邀出席此次活动。 本次大会以“拥抱开放”为主题,聚焦开放式创新,通过演讲分享、专场对接、需求发布…...
【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 类型的应用场景四、原生,序列化,哈希…...
网络安全应急响应典型案例-(DDOS类、僵尸网络类、数据泄露类)
一、DDOS类事件典型案例 DDOS攻击,即分布式拒绝服务攻击,其目的在于使目标电脑的网络或系统资源耗尽,使服务暂时中断或停止,导致其正常用户无法访问。CC攻击使用代理服务器向受害服务器发送大量貌似合法的请求(通常…...
【测试开发】Mq消息重复如何测试?
本篇文章主要讲述重复消费的原因,以及如何去测试这个场景,最后也会告诉大家,目前互联网项目关于如何避免重复消费的解决方案。 Mq为什么会有重复消费的问题? Mq 常见的缺点之一就是消息重复消费问题,产生这种问题的原因是什么呢…...
C++和C#程序语言的区别
一直学习C++和C#,两者之间的区别总结一下 目录 一、两种语言概述 C++语言 C#语言 二、两种语言对比 2.1运行依赖...
CentOS配置Java环境报错-bash: /usr/local/jdk1.8.0_381/bin/java: 无法执行二进制文件
CentOS配置Java环境后执行java -version时报错: -bash: /usr/local/jdk1.8.0_381/bin/java: 无法执行二进制文件原因是所使用的jdk的版本和Linux内核架构匹配不上 使用以下命令查看Linux架构: [rootlocalhost ~]# cat /proc/version Linux version 3.1…...
MySQL进阶 —— 超详细操作演示!!!(上)
MySQL进阶 —— 超详细操作演示!!!(上) 一、存储引擎1.1 MySQL 体系结构1.2 存储引擎介绍1.3 存储引擎特点1.4 存储引擎选择 二、索引2.1 索引概述2.2 索引结构2.3 索引分类2.4 索引语法2.5 SQL 性能分析2.6 索引使用2…...
一条爬虫抓取一个小网站所有数据
一条爬虫抓取一个小网站所有数据 今天闲来无事,写一个爬虫来玩玩。在网上冲浪的时候发现了一个搞笑的段子网,发现里面的内容还是比较有意思的,于是心血来潮,就想着能不能写一个Python程序,抓取几条数据下来看看&am…...
八大排序——快速排序
Hello,大家好,今天分享的八大排序里的快速排序,所谓快速排序是一个叫霍尔的人发明,有很多人可能会觉得为什么不叫霍尔排序,其中原因就是因为它快,快速则体现了它的特点,今天我们就来讲一下快速排…...
【ES】笔记-Class类剖析
Class Class介绍与初体验ES5 通过构造函数实例化对象ES6 通过Class中的constructor实列化对象 Class 静态成员实例对象与函数对象的属性不相通实例对象与函数对象原型上的属性是相通的Class中对于static 标注的对象和方法不属于实列对象,属于类。 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.绘图数…...
大模型应用开发:从Demo到生产,小白程序员必看!收藏这份实战指南
本文深入剖析了将大模型应用从原型阶段推向生产环境所面临的关键挑战,涵盖数据处理(格式多样性、切块策略、数据更新)、检索质量(找不到、找不准、找太多)、生成阶段(幻觉、引用溯源)、规模化工…...
深度学习 三次浪潮、三大驱动力与神经科学的恩怨(二)
1. 一个领域,多个名字 很多人以为"深度学习"是一个全新的领域。事实上,它的历史可以追溯到 20 世纪 40 年代——只不过在不同时期,它被叫过完全不同的名字: 1940s-1960s:被称为控制论(Cybernetic…...
从51job爬虫案例出发,聊聊如何用Selenium优雅地绕过前端反爬机制
从51job爬虫案例解析Selenium反反爬高阶策略 当招聘网站的前端技术不断升级,传统爬虫手段逐渐失效时,如何让自动化工具的行为更像真实用户?这个问题困扰着许多中高级开发者。以51job为例,其动态加载、URL不变的设计让常规爬虫束手…...
Qwen3-32B-Chat微调实战:提升OpenClaw代码生成任务的准确性
Qwen3-32B-Chat微调实战:提升OpenClaw代码生成任务的准确性 1. 为什么需要微调Qwen3-32B-Chat? 去年夏天,当我第一次尝试用OpenClaw自动化我的开发工作流时,遇到了一个令人沮丧的问题:模型生成的代码虽然语法正确&am…...
3KW无线充电系统设计:开环控制与闭环控制的MATLAB Simulink仿真模型,采用双边L...
3KW无线充电系统设计(MATLAB simulink仿真模型) 控制方式:开环控制闭环控制 拓扑结构:双边LCC拓扑结构 输入电压:750V 输出电压:400V 传输功率:3KW 最近在折腾一个3KW无线充电系统的仿真项目&am…...
STM32F103C8T6 DHT11温湿度监测系统 HAL库 CubeMX实战(避坑指南)
1. 项目背景与硬件选型 温湿度监测是物联网领域最基础也最实用的功能之一。我最近用STM32F103C8T6和DHT11搭建了一个环境监测节点,整个过程踩了不少坑,也积累了一些实战经验。这个方案特别适合需要低成本、快速上手的场景,比如智能家居、农业…...
Windows 11 下 3D Gaussian Splatting (3DGS) 环境配置与实战指南
1. Windows 11下的3DGS环境搭建全攻略 第一次接触3D Gaussian Splatting(简称3DGS)这个技术时,我完全被它惊艳到了。它能够从几张普通的照片重建出逼真的3D场景,而且渲染速度极快。不过说实话,在Windows 11上配置这个环…...
手把手教你用SteamCMD在Windows服务器上搭建Rust腐蚀私服(附详细参数配置)
手把手教你用SteamCMD在Windows服务器上搭建Rust腐蚀私服(附详细参数配置) 在生存游戏领域,Rust以其硬核的PVP机制和高度自由的沙盒玩法,持续吸引着大量玩家。对于想要掌控游戏规则、打造专属社区的管理员来说,自建服…...
ai全程护航:让快马智能助手帮你搞定proteus安装与初学难题
最近在折腾Proteus仿真软件时,发现从安装到入门会遇到不少"坑"。好在发现了InsCode(快马)平台的AI辅助功能,整个过程变得轻松多了。这里分享下如何用AI搞定Proteus全流程难题的实践心得。 智能安装诊断 第一次安装Proteus时,遇到许…...
Spring PetClinic实战解析:从单体应用到云原生部署的5大架构亮点
Spring PetClinic实战解析:从单体应用到云原生部署的5大架构亮点 【免费下载链接】spring-petclinic A sample Spring-based application 项目地址: https://gitcode.com/gh_mirrors/sp/spring-petclinic 你是否遇到过这样的困境:在学习Spring框架…...
