Java多线程系列——锁
0.引言
在并发编程中,锁是一种重要的同步机制,用于控制对共享资源的访问。Java 提供了多种锁的实现,每种锁都有不同的特性和适用场景。本文将深入介绍 Java 中常见的锁类型,包括内置锁、显式锁、读写锁等,并讨论它们的使用方法和最佳实践。
1. 内置锁(synchronized)
内置锁是 Java 中最基本的锁机制,通过 synchronized 关键字来实现。它可以用于同步方法或同步代码块,保证同一时间只有一个线程可以执行被锁定的代码,从而确保线程安全性。
public synchronized void synchronizedMethod() {// 同步方法体
}// 或者public void synchronizedBlock() {synchronized(this) {// 同步代码块}
}
内置锁的优点是简单易用,但缺点是粒度较粗,无法支持灵活的并发控制。
2. 显式锁(ReentrantLock)
ReentrantLock 是 Java 提供的显式锁实现,它提供了比内置锁更多的功能和灵活性。与 synchronized 相比,ReentrantLock 允许更灵活的加锁与释放锁操作,支持公平性和可中断性。
ReentrantLock lock = new ReentrantLock();lock.lock(); // 获取锁
try {// 临界区代码
} finally {lock.unlock(); // 释放锁
}
显式锁的优点是提供了更多的控制选项,适用于复杂的并发场景,但使用它需要显式地管理锁的获取和释放,容易出错。
3. 读写锁(ReentrantReadWriteLock)
ReentrantReadWriteLock 是一种特殊的锁,它分为读锁和写锁两种。多个线程可以同时持有读锁,但只有一个线程可以持有写锁。这种锁适用于读操作频繁、写操作较少的场景,可以提高并发性能。
ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
ReadWriteLock.ReadLock readLock = rwLock.readLock();
ReadWriteLock.WriteLock writeLock = rwLock.writeLock();// 读操作
readLock.lock();
try {// 读操作代码
} finally {readLock.unlock();
}// 写操作
writeLock.lock();
try {// 写操作代码
} finally {writeLock.unlock();
}
读写锁的优点是提高了读操作的并发性能,但在写操作频繁的情况下可能导致读操作的饥饿现象。
4. 其他锁
除了上述常见的锁类型外,Java 还提供了诸如StampedLock、Condition、Semaphore 等更多的锁实现,每种锁都有其特定的使用场景和适用性。
5. 锁的选择和最佳实践
在选择锁时,需要根据具体的业务需求和性能要求来进行权衡。一般来说:
- 如果只需要简单的同步控制,可以使用内置锁;
- 如果需要更多的控制选项和灵活性,可以使用显式锁;
- 如果读操作远远多于写操作,可以考虑使用读写锁;
- 对于特定场景,还可以选择其他类型的锁。
同时,在使用锁的过程中,需要注意避免死锁、锁竞争和锁粒度过大等问题,合理设计锁的获取顺序,并尽量减少锁的持有时间,以提高程序的并发性能和可维护性。
6.举例
假设我们有一个简单的银行账户类 BankAccount
,它包含账户余额和提款方法。多个线程可能同时访问同一个银行账户,我们需要确保在进行提款操作时只有一个线程能够访问账户并更新余额,以避免出现并发问题。
我们可以使用锁来控制对共享资源(即账户余额)的访问,确保在任何时候只有一个线程能够执行更新余额的操作。以下是一个使用内置锁(synchronized)的示例:
public class BankAccount {private double balance;public BankAccount(double initialBalance) {this.balance = initialBalance;}public synchronized void withdraw(double amount) {if (amount <= balance) {balance -= amount;System.out.println(Thread.currentThread().getName() + " withdraws $" + amount + ". Remaining balance: $" + balance);} else {System.out.println(Thread.currentThread().getName() + " tries to withdraw $" + amount + " but insufficient funds.");}}public double getBalance() {return balance;}
}
在这个示例中,withdraw()
方法被标记为 synchronized,这意味着只有一个线程可以同时访问该方法。当一个线程调用 withdraw()
方法时,其他线程必须等待直到当前线程执行完毕。
下面是一个简单的测试类,模拟了多个线程同时对银行账户进行提款操作:
public class Main {public static void main(String[] args) {BankAccount account = new BankAccount(1000);// 创建多个线程同时进行提款操作for (int i = 0; i < 5; i++) {Thread thread = new Thread(() -> {account.withdraw(200);});thread.start();}}
}
在这个例子中,即使有多个线程同时尝试提款,由于 withdraw()
方法被 synchronized 修饰,每次只有一个线程能够成功访问并更新账户余额,从而保证了线程安全性。
通过使用锁来控制对共享资源的访问,我们可以避免并发问题,确保多线程环境下程序的正确性和可靠性。
7. 结语
通过本文的介绍,我们了解了 Java 中常见的锁类型及其使用方法。锁是并发编程中重要的同步机制,合理选择和使用锁对于编写高性能、线程安全的并发程序至关重要。希望本文能够帮助读者更好地理解锁的概念和使用技巧,并在实践中运用到自己的项目中去。
相关文章:

Java多线程系列——锁
0.引言 在并发编程中,锁是一种重要的同步机制,用于控制对共享资源的访问。Java 提供了多种锁的实现,每种锁都有不同的特性和适用场景。本文将深入介绍 Java 中常见的锁类型,包括内置锁、显式锁、读写锁等,并讨论它们的…...

蓝牙BLE学习-GAP
1.概述 GAP层(Generic access profile-通用访问配置文件)。GAP是对LL层payload(有效数据包)如何进行解析的两种方式的一种,而且也是最简单的一种。GAP简单的对LL payload进行一些规范和定义,因此GAP能实现的…...

算法训练营day28(补), 贪心算法2
//122. 买卖股票的最佳时机 II func maxProfit(prices []int) int { result : 0 //利润总和 for i : 1; i < len(prices); i { if prices[i]-prices[i-1] > 0 { result result (prices[i] - prices[i-1]) } } return result } //55. 跳跃游戏 func canJump(nums []…...

Vue核心基础4:绑定样式、条件渲染、列表渲染
1 绑定样式 【代码】 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>绑定样式</title><s…...

go-zero读取mysql部分字段
读取部分字段,使用函数 QueryRowPartialCtx 。 假设有如下一张表: CREATE TABLE test (id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, ctime DATETIME);要读取字段 ctime 值。 定义一结构体: type X struct {state int db:"…...

反转一个单链表
反转一个单链表 题意:反转一个单链表。 示例: 输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL 思路 需要虚拟节点么? 答:不需要,因为没有删除节点,只是改变了节点的指向。 遍…...

拿捏c语言指针(中)
前言 书接上回 拿捏c语言指针(上) 此篇主要讲解的是指针与数组之间的爱恨情仇,跟着我的脚步一起来看看吧~ 创造不易,可以帮忙点点赞吗 如有差错,欢迎指出 理解数组名 数组名是首元素地址 例外 1.sizeof࿰…...

鸿蒙语言ArkTS(更好的生产力与性能)
ArkTS是鸿蒙生态的应用开发语言 ArkTS提供了声明式UI范式、状态管理支持等相应的能力,让开发者可以以更简洁、更自然的方式开发应用。 同时,它在保持TypeScript(简称TS)基本语法风格的基础上,进一步通过规范强化静态检…...

VBA技术资料MF120:打印固定标题行列
我给VBA的定义:VBA是个人小型自动化处理的有效工具。利用好了,可以大大提高自己的工作效率,而且可以提高数据的准确度。“VBA语言専攻”提供的教程一共九套,分为初级、中级、高级三大部分,教程是对VBA的系统讲解&#…...

MongoDB聚合运算符:$add
$add运算符将将数字相加或将数字和日期相加。如果参数之一是日期,则 $add会将其他参数视为毫秒,并添加到日期中。 语法 { $add: [ <expression1>, <expression2>, ... ] }参数可以是任何有效的表达式,只要能否解析为数值或日期…...

《剑指Offer》笔记题解思路技巧优化 Java版本——新版leetcode_Part_4
《剑指Offer》笔记&题解&思路&技巧&优化_Part_4 😍😍😍 相知🙌🙌🙌 相识😢😢😢 开始刷题1. LCR 148. 验证图书取出顺序——栈的压入、弹出序列2. LCR 14…...

数据库第四次实验
目录 1.建立数据表并插入数据 2 视图的创建 2.1 行列子集视图的创建 2.2 多表视图 2.3视图上建立视图 2.4分组视图 2.5带表达式的视图 3 删除视图 4 查询视图 5 更新视图 5.1 修改某一个属性 5.2 删除一条数据 5.3 插入…...

基于PPNSA+扰动算子的车间调度最优化matlab仿真,可以任意调整工件数和机器数,输出甘特图
目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 5.完整程序 1.程序功能描述 基于PPNSA扰动算子的车间调度最优化matlab仿真,可以任意调整工件数和机器数,输出甘特图和优化收敛曲线。 2.测试软件版本以及运行结果展示 MATLAB2022a版本运行…...

UnityShader——06UnityShader介绍
UnityShader介绍 UnityShader的基础ShaderLab UnityShader属性块介绍 Properties {//和public变量一样会显示在Unity的inspector面板上//_MainTex为变量名,在属性里的变量一般会加下划线,来区分参数变量和临时变量//Texture为变量命名//2D为类型&…...

人工智能学习与实训笔记(一):零基础理解神经网络
人工智能专栏文章汇总:人工智能学习专栏文章汇总-CSDN博客 本篇目录 一、什么是神经网络模型 二、机器学习的类型 2.1 监督学习 2.2 无监督学习 2.3 半监督学习 2.4 强化学习 三、网络模型结构基础 3.1 单层网络 编辑 3.2 多层网络 3.3 非线性多层网络…...

LeetCode刷题小记 一、【数组】
LeetCode刷题小记 一、【数组】 文章目录 LeetCode刷题小记 一、【数组】写在前面1. 数组1.1 理论基础1.2 二分查找1.3 移除元素1.4 有序数组的平方1.5 长度最小的子数组1.6 螺旋矩阵II Reference 写在前面 本系列笔记主要作为笔者刷题的题解,所用的语言为Python3&…...

iOS总体框架介绍和详尽说明
iOS是由苹果公司开发的移动操作系统,为iPhone、iPad、iPod Touch等设备提供支持。iOS采用了基于Unix的核心(称为Darwin),并采用了类似于Mac OS X的图形用户界面。以下是iOS的总体框架介绍和详尽说明: UIKit框架&#…...

【C++】const与constexpr详解
1. constexpr:常量表达式 所谓常量表达式,指的就是由多个(≥1)常量组成的表达式。换句话说,如果表达式中的成员都是常量,那么该表达式就是一个常量表达式。这也意味着,常量表达式一旦确定,其值将无法修改。 实际开发中,我们经常会…...

蓝桥杯:日期统计讲解(C++)
日期统计 本题来自于:2023年十四届省赛大学B组真题 主要考察:暴力。 代码放在下面,代码中重要的细节全都写了注释,非常清晰明了: #include <bits/stdc.h> //万能头文件 using namespace std;int main() {…...

Python re.findall()中的正则表达式包含多个括号时的返回值——包含元组的列表
当re.findall()中的正则表达式包含多个括号时,返回值是一个列表,其中每个元素都是一个元组。这个元组的长度与正则表达式中括号的数量相同,元组中的每个元素都是与相应括号中的模式匹配的文本。 import re # 定义一个包含三个括号的正则表达…...

Python——列表
一、列表的特性介绍 列表和字符串⼀样也是序列类型的数据 列表内的元素直接⽤英⽂的逗号隔开,元素是可变的,所以列表是可变的数据类型,⽽字符串不是。 列表的元素可以是 Python 中的任何类型的数据对象。如:字符串、…...

无人机图像识别技术研究及应用,无人机AI算法技术理论,无人机飞行控制识别算法详解
在现代科技领域中,无人机技术是一个备受瞩目的领域。随着人们对无人机应用的需求在不断增加,无人机技术也在不断发展和改进。在众多的无人机技术中,无人机图像识别技术是其中之一。 无人机图像识别技术是利用计算机视觉技术对无人机拍摄的图像…...

清华AutoGPT:掀起AI新浪潮,与GPT4.0一较高下
引言: 随着人工智能技术的飞速发展,自然语言处理(NLP)领域迎来了一个又一个突破。最近,清华大学研发的AutoGPT成为了业界的焦点。这款AI模型以其出色的性能,展现了中国在AI领域的强大实力。 目录 引言&…...

人工智能学习与实训笔记(二):神经网络之图像分类问题
人工智能专栏文章汇总:人工智能学习专栏文章汇总-CSDN博客 目录 二、图像分类问题 2.1 尝试使用全连接神经网络 2.2 引入卷积神经网络 2.3 分类函数Softmax 2.4 交叉熵损失函数 2.5 学习率优化算法 2.6 图像预处理算法 2.6.1 随机改变亮暗、对比度和颜色等 …...

SSM框架,spring-aop的学习
代理模式 二十三种设计模式中的一种,属于结构型模式。它的作用就是通过提供一个代理类,让我们在调用目标方法的时候,不再是直接对目标方法进行调用,而是通过代理类间接调用。让不属于目标方法核心逻辑的代码从目标方法中剥离出来…...

【设计模式】4、策略模式
文章目录 一、问题二、解决方案2.1 真实世界的类比2.2 策略模式结构2.3 适用场景2.4 实现方式2.5 优缺点2.6 与其他模式的关系 三、示例代码3.1 go3.2 rust 策略模式是一种行为设计模式,它能定义一系列算法,把每种算法分别放入独立的类中,以是…...

【C++学习手札】多态:掌握面向对象编程的动态绑定与继承机制(深入)
🎬慕斯主页:修仙—别有洞天 ♈️今日夜电波:世界上的另一个我 1:02━━━━━━️💟──────── 3:58 🔄 ◀️ ⏸ ▶️ ☰ &am…...

【机构vip教程】Android SDK手机测试环境搭建
Android SDK 的安装和环境变量的配置 前置条件:需已安装 jdk1.8及 以上版本 1、下载Android SDK,解压后即可(全英文路径);下载地址:http://tools.android-studio.org/index.php/sdk 2、新建一个环境变量&…...

2024.2.18
使用fgets统计给定文件的行数 #include<stdio.h> #include<string.h> int main(int argc, const char *argv[]) {FILE *fpNULL;if((fpfopen("./test.txt","w"))NULL){perror("open err");return -1;}fputc(h,fp);fputc(\n,fp);fput…...

Haproxy实验
环境: servera(Haproxy):192.168.233.132 serverb(web1):192.168.233.144 serverc(web2):192.168.233.140 serverd(客户端):192.168.233.141 servera(Haproxy): yum install haproxy -y vim /etc/haproxy/haproxy.cfg(配置文件) # 设置日志&#…...