【Java 基础】21 多线程同步与锁
文章目录
- 1.存在的问题
- 2.使用同步解决问题
- 1) synchronized
- 2) volatile
- 3) 锁
- 总结
用多线程过程中,有可能出现 多个线程同时处理(获取或修改等)同一个数据,这个时候就 会发生数据不同步的问题, 因此出现了同步和锁来保证多个线程可以安全的处理同一个数据。
1.存在的问题
例如:火车售票窗口售票,假如我们有 2 个窗口(相当于开启了 2 个线程),同时卖 10 张票
两个窗口的操作流程都如下:
1)购票者到窗口
2)窗口访问票匣子获取余票数量
3)票匣子返回余票数量
4)售票窗口判断,若存在票(大于 0 )则卖给他
那么,假如票匣子就剩下 1 张票啦!但是不巧的是两个窗口同时查看余票数量,都发现还有 1 张票,**又都卖给了购票者……**这就是多线程存在的数据不同步问题_
示例代码:
public class Demo {public static void main(String[] args) throws InterruptedException {ThreadDemo threadDemo = new ThreadDemo();new Thread(threadDemo,"售票窗口1").start();new Thread(threadDemo,"售票窗口2").start();}
}class ThreadDemo implements Runnable {private int ticketCount = 10;@Overridepublic void run() {String tName = Thread.currentThread().getName();while (true) {if (ticketCount <= 0) {return;}try {Thread.sleep(200);System.out.println(tName + "成功卖了一张票!余票:" + (ticketCount-- - 1));} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}
输出结果:
售票窗口1成功卖了一张票!余票:9
售票窗口2成功卖了一张票!余票:8
售票窗口1成功卖了一张票!余票:7
售票窗口2成功卖了一张票!余票:6
售票窗口2成功卖了一张票!余票:5
售票窗口1成功卖了一张票!余票:4
售票窗口2成功卖了一张票!余票:3
售票窗口1成功卖了一张票!余票:2
售票窗口1成功卖了一张票!余票:1
售票窗口2成功卖了一张票!余票:0
售票窗口1成功卖了一张票!余票:-1
2.使用同步解决问题
同步(Synchronization) 是一种协调多个线程执行的机制,它能够确保在同一时刻只有一个线程访问共享资源。主要通过关键字 synchronized
和 volatile
以及 锁对象
等手段来实现同步。
1) synchronized
关键字 synchronized
用于修饰方法或代码块,保证在同一时刻只有一个线程能够执行被 synchronized
修饰的代码。以下是两种使用方式:
- 修饰方法
public synchronized void test() {// 同步的代码块
}
- 修饰代码块
public void someMethod() {// 非同步的代码块synchronized (lockObject) {// 同步的代码块}// 非同步的代码块
}
2) volatile
关键字 volatile
用于声明变量,保证变量的可见性。被 volatile
修饰的变量对所有线程可见,当一个线程修改了这个变量的值,其他线程能够立即看到修改后的值。
public class Demo {private volatile int ticketCount = 10;
}
3) 锁
Java 提供了很多种锁,常用的有 synchronized
关键字、ReentrantLock
及 Read/Write Lock
等 。
-
ReentrantLock
它支持可重入锁,允许一个线程多次获取同一把锁。
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock;public class Demo {private final Lock lock = new ReentrantLock();public void test() {lock.lock();try {// 同步的代码块} finally {lock.unlock();}} }
ReentrantLock
提供了比synchronized
更丰富的功能,如可中断锁、公平锁、定时锁等 -
Read/Write Lock
ReadWriteLock
接口定义了读写锁,它包含两个锁,一个用于读操作,一个用于写操作。读写锁允许多个线程同时读取共享资源,但只允许一个线程进行写操作。ReentrantReadWriteLock
是ReadWriteLock
的一个实现类import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock;public class Demo {private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();public void read() {readWriteLock.readLock().lock();try {// 读取共享资源的操作} finally {readWriteLock.readLock().unlock();}}public void write() {readWriteLock.writeLock().lock();try {// 修改共享资源的操作} finally {readWriteLock.writeLock().unlock();}} }
读写锁适用于读操作远远多于写操作的场景,可以提高并发性
-
StampedLock
它是一种读写锁的变种,提供了乐观读锁,可以在读多写少的场景中提供更好的性能
import java.util.concurrent.locks.StampedLock;public class Demo {private final StampedLock stampedLock = new StampedLock();public void read() {long stamp = stampedLock.tryOptimisticRead();// 乐观读操作if (!stampedLock.validate(stamp)) {// 有写操作发生,转为悲观读stamp = stampedLock.readLock();try {// 悲观读操作} finally {stampedLock.unlockRead(stamp);}}}public void write() {long stamp = stampedLock.writeLock();try {// 写操作} finally {stampedLock.unlockWrite(stamp);}} }
StampedLock
提供了更细粒度的控制,并允许在不同的代码路径中执行不同的操作
总结
在多线程编程中,确保线程安全是至关重要的!通过合理使用 synchronized
关键字、volatile
关键字以及 ReentrantLock
等锁机制,可以有效地保护共享资源,避免数据不一致和竞态条件等问题。合理的同步机制不仅能够提高程序的性能,还能够确保程序的正确性。在实际开发中,根据具体场景选择合适的同步和锁机制是编写高效、安全多线程代码的关键。
相关文章:

【Java 基础】21 多线程同步与锁
文章目录 1.存在的问题2.使用同步解决问题1) synchronized2) volatile3) 锁 总结 用多线程过程中,有可能出现 多个线程同时处理(获取或修改等)同一个数据,这个时候就 会发生数据不同步的问题, 因此出现了同步和锁来…...

Python语言基础知识(一)
文章目录 1、Python内置对象介绍2、标识符与变量3、数据类型—数字4、数据类型—字符串与字节串5、数据类型—列表、元组、字典、集合6、运算符和表达式7、运算符和表达式—算术运算符8、运算符和表达式—关系运算符9.1、运算符和表达式— 成员测试运算符in9.2、运算符和表达式…...

Xilinx FPGA平台DDR3设计详解(三):DDR3 介绍
本文介绍一下常用的存储芯片DDR3,包括DDR3的芯片型号识别、DDR3芯片命名、DDR3的基本结构等知识,为后续掌握FPGA DDR3的读写控制打下坚实基础。 一、DDR3芯片型号 电路板上的镁光DDR3芯片上没有具体的型号名。 如果想知道具体的DDR3芯片型号&#…...
字典的遍历
字典不是有序的集合,就不能通过index来遍历了,那如何遍历字典呢? 方法一:直接用字典 for key in a_dict: print a_dict[key] 通过这样的结构可以的。 d {"liming" : 98, "wangli":95, "mali":90, "liping&q…...

Linux环境下的MySQL安装
文章目录 前提说明1.卸载内置环境2.检查系统安装包3.卸载这些默认安装包4.获取MySQL官方yum源5.安装MySQLyum源,对比前后yum源6.查看yum源是否生效7.安装MySQL服务8.查看相对应的配置文件9.启动服务10.查看启动服务11.登录方法一12.登录方法二13.登录方法三14.设置开…...
梦想与魔法:编程之路的挑战与荣耀
在年少轻狂的岁月里,我们都有过一些不切实际的梦想,渴望成为某种神奇的存在。我的梦想是成为一名神奇的码农,用键盘编织魔法,创造出炫酷的虚拟世界。然而,现实是残酷的,当我刚入门计算机领域时,…...

qt 5.15.2 主窗体菜单工具栏树控件功能
qt 5.15.2 主窗体菜单工具栏树控件功能 显示主窗体效果: mainwindow.h文件内容: #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow> #include <QFileDialog> #include <QString> #include <QMessageBox>#inc…...

Day15——File类与IO流
1.java.io.File类的使用 1.1 File类的理解 File 类及本章下的各种流,都定义在 java.io 包下。一个 File 对象代表硬盘或网络中可能存在的一个文件或者文件目录(俗称文件夹),与平台无关。(体会万事万物皆对象…...

【Qt】QLineEdit显示输入十六进制,位数不足时按照规则填充显示及每两个字符以空格填充
问题 在实际开发中,有时候需要对输入进行限制,一是更加合理,二是防止出现误操作。 比如: 使用Qt进行应用程序开发时,对单行编辑框QLineEdit控件,设置只可输入十六进制。 限制输入的方式常用且经典的是使用…...

GPT 中文提示词技巧:参照 OpenAI 官方教程
前言 搜了半天什么 prompt engineering 的课,最后会发现 gpt 官方其实是有 prompt 教程的。因此本文主要是学习这篇教程。 概述 - OpenAI API 部分案例是参考:根据吴恩达老师教程总结出中文版prompt教程_哔哩哔哩_bilibili up主的内容。 一、尽可能清…...

原生微信小程序将字符串生成二维码图片
weapp-qrcode.js再最后 inde.ts中的内容 // pages/qrCode/index.ts // 引入weapp-qrcode.js文件 var QRCode require(../../utils/weapp-qrcode) Page({/*** 页面的初始数据*/data: {orderNo:"",imagePath:},/*** 生命周期函数--监听页面加载*/onLoad(options:any)…...
深入理解HTTPS加密协议
在现代网络环境中,数据安全和隐私保护至关重要。HTTPS(全称为HyperText Transfer Protocol Secure)是一种用于保障互联网通信安全的加密协议,它通过在HTTP协议的基础上添加SSL/TLS层来实现对数据的加密传输。本文将详细介绍HTTPS的…...

路径规划之PRM算法
系列文章目录 路径规划之Dijkstra算法 路径规划之Best-First Search算法 路径规划之A *算法 路径规划之D *算法 路径规划之PRM算法 路径规划之PRM算法 系列文章目录前言一、前期准备1.栅格地图2.采样3.路标 二、PRM算法1.起源2.流程3. 优缺点4. 实际效果 前言 之前提到的几种…...

深入理解数据在内存中是如何存储的,位移操作符如何使用(能看懂文字就能明白系列)文章超长,慢慢品尝
系列文章目录 C语言笔记专栏 能看懂文字就能明白系列 🌟 个人主页:古德猫宁- 🌈 信念如阳光,照亮前行的每一步 文章目录 系列文章目录🌈 *信念如阳光,照亮前行的每一步* 前言引子一、2进制和进制转化为什么…...

ArcGIS提示当前许可不支持影像服务器
1、问题: 在用ArcGIS上处理影像栅格数据时(比如栅格数据集裁剪、镶嵌数据集构建镶嵌线等)经常会出现。 无法启动配置 RasterComander.ImageServer <详信息 在计算机XXXXX上创建服务器对象实例失败 当前许可不支持影像服务器。 ArcGIS提示当…...

Android P 9.0 增加以太网静态IP功能
效果图 一、Settings添加以太网的配置: 1、vendor\mediatek\proprietary\packages\apps\MtkSettings\res\xml\network_and_internet.xml <com.android.settingslib.RestrictedPreferenceandroid:key"ethernet_settings"android:title"string/et…...

Android12之MediaCodec硬编解码调试手段(四十九)
简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 优质专栏:多媒体系统工程师系列【原创干货持续更新中……】🚀 人生格言: 人生从来没有捷径,只…...

2.Ansible的copy模块,我最常用的模块
1. 简述 先从我自身的情况来说,我不是运维人员,并且对linux操作也不是特别熟悉,所以工作中我使用ansible基本就是在平常的自动化部署中,而使用最多的模块就是copy模块。我使用copy模块也主要是来替换生产环境的配置文件。所以&am…...

python程序将部分文件复制到指定目录
geotools-28.2中的lib一共有264个jar包,但我只想将部分100个左右jar包引导我的环境中,那个就需要从目录中找出想要的那100个jar,手动挑选太费时间,我简单的写了个小脚本来实现。 我将想要的jar文件名和路径存放到txt中࿰…...

5分钟教你利用服务器,打造1个 7*24H直播的直播间
最近在折腾无人直播。觉得还挺有意思,接下来就分享一下如何实现。实现后就可以给一些主流的平台直播间不间断推流,达到无人直播的效果。 前提:拥有一台服务器。最好流量是1T或者以上。直播对流量要求比较高,视频码率越大ÿ…...

RocketMQ延迟消息机制
两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数,对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后…...
React Native 开发环境搭建(全平台详解)
React Native 开发环境搭建(全平台详解) 在开始使用 React Native 开发移动应用之前,正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南,涵盖 macOS 和 Windows 平台的配置步骤,如何在 Android 和 iOS…...

微信小程序 - 手机震动
一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注:文档 https://developers.weixin.qq…...

2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面
代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口(适配服务端返回 Token) export const login async (code, avatar) > {const res await http…...

ServerTrust 并非唯一
NSURLAuthenticationMethodServerTrust 只是 authenticationMethod 的冰山一角 要理解 NSURLAuthenticationMethodServerTrust, 首先要明白它只是 authenticationMethod 的选项之一, 并非唯一 1 先厘清概念 点说明authenticationMethodURLAuthenticationChallenge.protectionS…...
JDK 17 新特性
#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持,不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的ÿ…...

推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材)
推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材) 这个项目能干嘛? 使用 gemini 2.0 的 api 和 google 其他的 api 来做衍生处理 简化和优化了文生图和图生图的行为(我的最主要) 并且有一些目标检测和切割(我用不到) 视频和 imagefx 因为没 a…...

【JVM面试篇】高频八股汇总——类加载和类加载器
目录 1. 讲一下类加载过程? 2. Java创建对象的过程? 3. 对象的生命周期? 4. 类加载器有哪些? 5. 双亲委派模型的作用(好处)? 6. 讲一下类的加载和双亲委派原则? 7. 双亲委派模…...
为什么要创建 Vue 实例
核心原因:Vue 需要一个「控制中心」来驱动整个应用 你可以把 Vue 实例想象成你应用的**「大脑」或「引擎」。它负责协调模板、数据、逻辑和行为,将它们变成一个活的、可交互的应用**。没有这个实例,你的代码只是一堆静态的 HTML、JavaScript 变量和函数,无法「活」起来。 …...

从0开始学习R语言--Day17--Cox回归
Cox回归 在用医疗数据作分析时,最常见的是去预测某类病的患者的死亡率或预测他们的结局。但是我们得到的病人数据,往往会有很多的协变量,即使我们通过计算来减少指标对结果的影响,我们的数据中依然会有很多的协变量,且…...