Java进阶篇--公平锁 非公平锁

目录

ReentrantLock的介绍

重入性的实现原理

代码示例:

公平锁与非公平锁

代码示例:


ReentrantLock的介绍

ReentrantLock是Java中实现Lock接口的一种重入锁(Reentrant Lock)实现类。它提供了与synchronized关键字相似的功能(在java关键字synchronized隐式支持重入性,synchronized通过获取自增,释放自减的方式实现重入),但比synchronized更加灵活和强大。ReentrantLock可以用于实现多线程之间的互斥访问和同步操作。

下面是ReentrantLock主要的特点和用法介绍:

1、可重入性:
ReentrantLock允许同一个线程多次获取同一把锁,而不会发生死锁。这种特性叫做可重入性,也就是可重入锁。当一个线程已经持有锁时,再次获取锁时会增加锁的计数器,每次释放锁时计数器减一,只有当计数器为0时锁才会完全释放。

2、获取锁的方式:
通过调用ReentrantLock的lock()方法可以获取锁,如果锁已被其他线程获取,则当前线程会被阻塞,直到获取到锁为止。另外,ReentrantLock还提供了tryLock()方法,该方法尝试立即获取锁,如果锁未被其他线程获取,则返回true,否则返回false。tryLock()方法也可以设置超时时间,指定在一段时间内尝试获取锁。

3、线程阻塞与唤醒:
与synchronized不同,ReentrantLock提供了显示的线程挂起和恢复的能力。通过调用lock()方法后,如果锁已被其他线程占用,则当前线程会进入休眠状态,直到获取锁成功。当不再需要锁时,需要调用unlock()方法显式地释放锁,以唤醒其他等待线程。

4、锁的公平性:
ReentrantLock支持公平锁和非公平锁。可以在创建ReentrantLock对象时指定构造函数参数来设置锁的公平性。默认情况下,ReentrantLock是非公平锁。

5、条件变量:
与ReentrantLock配套使用的还有条件变量(Condition),可以通过ReentrantLock的newCondition()方法创建一个特定的条件变量。条件变量可以使线程在某个条件满足时等待,或者唤醒等待在该条件上的线程。

总结:
ReentrantLock是Java提供的一种可重入的高级锁实现,相比synchronized关键字更加灵活,并提供了更多功能,如可控的锁公平性、显示的线程挂起和恢复、超时锁等等。在多线程编程中,使用ReentrantLock可以更好地控制资源的访问和线程的同步。。

那么,要想完完全全的弄懂ReentrantLock的话,主要也就是ReentrantLock同步语义的学习:1. 重入性的实现原理;2. 公平锁和非公平锁。

重入性的实现原理

ReentrantLock支持重入性的实现是通过在锁对象上维护一个计数器来实现的。

在Java中,每个线程都拥有自己的线程栈(Thread Stack)。如果一个方法被调用,那么就会在线程栈上分配一段空间,这段空间被称为栈帧(Stack Frame)。当一个方法被另一个方法调用时,就会在栈上分配另一个栈帧,这就形成了方法调用链。

在ReentrantLock中,每个线程都有一个变量叫做"holdCount",表示该线程目前获取锁的次数。当一个线程第一次获取锁时,holdCount的值为1;如果再次获取锁,则holdCount递增,以此类推。当这个线程释放锁时,holdCount递减。只有当holdCount的值为0时,锁才真正释放。

也就是说,ReentrantLock对于每个线程来说,维护了一个独立的计数器。当一个线程试图获取锁时,首先判断当前线程是否已经获取了该锁,如果已经获取,则直接返回true;如果未获取,则需要尝试获取锁。当该线程成功获取锁时,holdCount递增,表示当前线程又多获取了一次该锁。当该线程释放锁时,holdCount递减,表示当前线程又释放了一次该锁。只有当holdCount的值为0时,锁才会真正释放。

总结一下:在ReentrantLock中,通过为每个线程维护一个计数器(即holdCount)来实现重入性。当一个线程多次获取锁时,计数器递增;每次释放锁时,计数器相应地递减。只有当计数器为0时,锁才完全释放,其他线程才能获取该锁。这种机制可以避免死锁和线程饥饿等问题,是一种非常有效的并发控制手段。

代码示例:

import java.util.concurrent.locks.ReentrantLock;public class Main {private ReentrantLock lock = new ReentrantLock();private double balance;public Main(double initialBalance) {balance = initialBalance;}// 存款public void deposit(double amount) {lock.lock(); // 获取锁try {balance += amount; // 更新余额System.out.println("存款成功,当前余额为:" + balance);} finally {lock.unlock(); // 释放锁}}// 取款public void withdraw(double amount) {lock.lock(); // 获取锁try {if (balance >= amount) {balance -= amount; // 更新余额System.out.println("取款成功,当前余额为:" + balance);} else {System.out.println("余额不足");}} finally {lock.unlock(); // 释放锁}}public static void main(String[] args) {Main account = new Main(1000.0);// 创建两个线程模拟两个用户进行存款和取款操作Thread depositThread = new Thread(() -> {for (int i = 0; i < 5; i++) {account.deposit(100.0);}});Thread withdrawThread = new Thread(() -> {for (int i = 0; i < 5; i++) {account.withdraw(200.0);}});depositThread.start();withdrawThread.start();}
}

在这个例子中,我们模拟了一个银行账户,通过使用ReentrantLock实现对余额的线程安全操作。在存款和取款方法中,我们都先获取锁,执行相应的操作,然后释放锁。

在主函数中,我们创建了两个线程,一个用于模拟存款操作,另一个用于模拟取款操作。每个线程都执行5次操作。通过使用ReentrantLock,我们确保了每次存款或取款操作是原子的,不会发生并发访问的问题。同时,由于ReentrantLock支持重入性,所以同一个线程可以多次获取锁,这也是允许的。

公平锁与非公平锁

公平锁和非公平锁是ReentrantLock(可重入锁)中的两种获取锁的策略。

  • 公平锁在多线程竞争下,按照线程的请求顺序来获取锁,保证了请求资源时间上的绝对顺序。当一个线程释放锁后,同步队列中的第一个等待线程会获取到锁,其他线程按照先来后到的顺序依次获取锁。公平锁可以避免线程的饥饿现象,但可能因为频繁的上下文切换而降低系统的吞吐量。
  • 非公平锁在多线程竞争下,不保证线程获取锁的顺序。当一个线程释放锁后,新的线程有机会直接获取到锁,即使其他线程在等待获取锁。非公平锁的实现较为简单高效,可以减少上下文切换的次数,提高系统的吞吐量。但是,某些线程可能会长时间等待,造成其他线程一直获取锁的情况。

ReentrantLock默认是非公平锁,因为在大多数情况下,非公平锁性能更好。如果需要创建公平锁,可以通过构造函数参数传入true,创建一个公平锁的实例:

ReentrantLock fairLock = new ReentrantLock(true); // 创建公平锁
//注意:使用ReentrantLock类创建非公平锁时,不需要传入参数或者传入false

需要注意的是,公平锁会增加一定的上下文切换次数,适用于对线程获取锁的顺序有严格要求的场景,而非公平锁适用于追求更高吞吐量的场景。

总结:

  • 公平锁按照线程申请锁的顺序获取锁,避免线程饥饿,但可能会引起较大的开销。
  • 非公平锁没有严格的获取顺序,可能会导致后申请的线程先获取锁,但能够获得更高的吞吐量。

在实际应用中,选择公平锁还是非公平锁取决于具体的场景和需求。一般来说,在线程竞争激烈的情况下,非公平锁可能更适合,可以提升系统性能。

代码示例:

import java.util.concurrent.locks.ReentrantLock;public class Main {private static ReentrantLock fairLock = new ReentrantLock(true);  // 创建公平锁private static ReentrantLock nonFairLock = new ReentrantLock(false);  // 创建非公平锁public static void main(String[] args) {// 公平锁示例Thread fairThread1 = new Thread(() -> {fairLock.lock();  // 获取公平锁try {System.out.println("公平锁 - 线程1获取到锁");Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();} finally {fairLock.unlock();  // 释放公平锁System.out.println("公平锁 - 线程1 释放公平锁");}});Thread fairThread2 = new Thread(() -> {fairLock.lock();  // 获取公平锁try {System.out.println("公平锁 - 线程2获取到锁");Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();} finally {fairLock.unlock();  // 释放公平锁System.out.println("公平锁 - 线程2 释放公平锁");}});// 非公平锁示例Thread nonFairThread1 = new Thread(() -> {nonFairLock.lock();  // 获取非公平锁try {System.out.println("非公平锁 - 线程1获取到锁");Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();} finally {nonFairLock.unlock();  // 释放非公平锁System.out.println("非公平锁 - 线程1 释放非公平锁");}});Thread nonFairThread2 = new Thread(() -> {nonFairLock.lock();  // 获取非公平锁try {System.out.println("非公平锁 - 线程2获取到锁");Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();} finally {nonFairLock.unlock();  // 释放非公平锁System.out.println("非公平锁 - 线程2 释放非公平锁");}});fairThread1.start();fairThread2.start();nonFairThread1.start();nonFairThread2.start();}
}

在上述示例中,我们创建了一个公平锁(fairLock)和一个非公平锁(nonFairLock),然后创建了两个线程来演示这两种锁的行为。

  • 对于公平锁,我们创建了两个线程(fairThread1和fairThread2),它们按照先来后到的顺序获取锁。由于是公平锁,fairThread1首先获取到锁,然后才轮到fairThread2获取锁。
  • 对于非公平锁,我们创建了两个线程(nonFairThread1和nonFairThread2),它们竞争获取锁。由于是非公平锁,在某些情况下,刚释放锁的线程有机会再次获取锁,因此可能会导致其他线程长时间等待。

通过运行上述代码,我们可以观察到公平锁和非公平锁的不同行为。请注意,输出结果可能因操作系统调度而有所差异,但公平锁会按照线程的请求顺序获取锁,而非公平锁则不保证顺序。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/105901.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Stable Diffusion绘画,卡通,教室

1 girl, parted lips, blush, makeup, light smile, school uniform, classroom, light rays, glow, thighs, collarbone, narrow waist, (masterpiece), wallpaper 1个女孩&#xff0c;双唇&#xff0c;腮红&#xff0c;化妆&#xff0c;浅笑&#xff0c;校服&#xff0c;教室…

单链表经典OJ题

目录 ​编辑 题目&#xff1a; 一、移除链表元素&#xff1a; 本质&#xff1a; 解题思路&#xff1a; 本题分为两种解法&#xff1a; 我们使用解法二&#xff1a; 注意事项&#xff1a; 完整代码&#xff1a; 题目&#xff1a; 一、移除链表元素&#xff1a; 本质&…

C++11智能指针

目录 一、什么是智能指针&#xff1f;二、为什么需要智能指针&#xff1f;三、内存泄漏3.1 什么是内存泄漏&#xff1f;内存泄漏的危害是什么&#xff1f;3.2 内存泄漏的分类3.3 如何检测内存泄漏&#xff1f;3.4 如何避免内存泄漏&#xff1f; 四、智能指针的使用及原理4.1 RA…

Kotlin vs Java:为什么Springboot官方教程选择了Kotlin?

导语 作为Java开发者的你&#xff0c;是否在为寻找Java的替代品而烦恼&#xff1f;担心受知识产权问题困扰&#xff1f;别担心&#xff0c;Kotlin来了&#xff01;它是你的救星&#xff0c;也是Springboot官网教程的选择。想知道为什么吗&#xff1f;那就往下翻吧&#xff01;…

【算法与数据结构】--常见数据结构--栈和队列

一、栈 栈&#xff08;Stack&#xff09; 是一种基本的数据结构&#xff0c;具有后进先出&#xff08;LIFO&#xff09;的特性&#xff0c;类似于现实生活中的一叠盘子。栈用于存储一组元素&#xff0c;但只允许在栈顶进行插入&#xff08;入栈&#xff09;和删除&#xff08;…

NeurIPS 2023 | MQ-Det: 首个支持多模态查询的开放世界目标检测大模型

目前的开放世界目标检测模型大多遵循文本查询的模式&#xff0c;即利用类别文本描述在目标图像中查询潜在目标。然而&#xff0c;这种方式往往会面临“广而不精”的问题。一图胜千言&#xff0c;为此&#xff0c;作者提出了基于多模态查询的目标检测&#xff08;MQ-Det&#xf…

傅里叶变换和其图像处理中的应用

以下部分文字资料整合于网络&#xff0c;本文仅供自己学习用&#xff01; 一、为什么要在频域进行图像处理&#xff1f; 一些在空间域表述困难的增强任务&#xff0c;在频率域中变得非常普通 滤波在频率域更为直观&#xff0c;你想想嘛&#xff0c;所谓滤波&#xff0c;就是…

KOSMOS-2.5:密集文本的多模态读写模型

Overview 总览摘要1 引言2 KOSMOS-2.52.1 模型结构2.1 图像和文本表征2.3 预训练数据2.4 数据处理2.5 过滤与质量控制 3 实验3.1 评估3.2 实现细节3.3 结果3.4 讨论 4 相关工作4.1 多模态大语言模型4.2 图文理解 5 总结与展望 总览 题目: KOSMOS-2.5: A Multimodal Literate M…

通过jsoup抓取谷歌商店评分

文章目录 背景实现是否下架预警评分 总的工具类,测试 背景 在谷歌上面发布包,有时候要看看评分,有时候会因为总总原因被下架,希望后台能够对评分进行预警,和下架预警 实现 测试地址: https://play.google.com/store/apps/details?idcom.tencent.mm 通过jsoup解析页面,然后获…

Python学习----Day07

函数 函数是组织好的&#xff0c;可重复使用的&#xff0c;用来实现单一&#xff0c;或相关联功能的代码段。函数能提高应用的模块性&#xff0c;和代码的重复利用率。你已经知道Python提供了许多内建函数&#xff0c;比如print()。但你也可以自己创建函数&#xff0c;这被叫做…

苍穹外卖(五) 微信小程序

项目应用: 使用微信小程序完成客户端开发并基于微信登录实现小程序的登录功能如果是新用户需要自动完成注册 微信小程序开发 介绍 小程序是一种新的开放能力&#xff0c;开发者可以快速地开发一个小程序。可以在微信内被便捷地获取和传播&#xff0c;同时具有出色的使用体验…

C# 图解教程 第5版 —— 第3章 C# 编程概述

文章目录 3.1 一个简单的 C# 程序&#xff08;*&#xff09;3.2 标识符3.3 关键字3.4 Main&#xff1a;程序的起始点&#xff08;*&#xff09;3.5 空白3.6 语句&#xff08;*&#xff09;3.7 从程序中输出文本3.7.1 Write&#xff08;*&#xff09;3.7.2 WriteLine&#xff08…

【C++进阶】:C++类型转换

C类型转换 一.C语言里的类型转换二.C语音类型转换的一些弊端三.C的四种类型转换1.static_cast2.reinterpret_cast3.const_cast4.dynamic_cast 一.C语言里的类型转换 在C语言中&#xff0c;如果赋值运算符左右两侧类型不同&#xff0c;或者形参与实参类型不匹配&#xff0c;或者…

Python实验三

1&#xff1a;编程统计英文句子中的单词出现的次数。 要求&#xff1a;输出结果为按照单词在句子中出现的次数降序排列。 提示&#xff1a;用split&#xff08;&#xff09;拆分字符串 # 1&#xff1a;编程统计英文句子中的单词出现的次数。 # 要求&#xff1a;输出结果为按照…

Dijkstra求最短路(图解)

你好&#xff0c;我是Hasity。 今天分享的内容&#xff1a;Dijkstra求最短路这个题目 Dijkstra求最短路I 题目描述 给定一个 n个点 m 条边的有向图&#xff0c;图中可能存在重边和自环&#xff0c;所有边权均为正值。 请你求出 1 号点到 n号点的最短距离&#xff0c;如果无…

Windows 中环境变量的查看与设置

接触了LLM应用开发后&#xff0c;经常要用到环境变量的设置&#xff08;openAI apikey啥的&#xff09; 但是老忘记&#xff0c;今天来学习和总结一下 主要用到以下几种&#xff1a;使用 PowerShell、CMD 和 Python 来查看和设置环境变量 文章目录 1. PowerShell查看环境变量&a…

【Linux】HTTP协议

文章目录 &#x1f4d6; 前言1. 认识URL && 引入http协议2. http协议格式2.1 宏观格式&#xff1a;2.2 实验演示&#xff1a; 3. http的方法3.1 GET方法&#xff1a;3.2 POST方法&#xff1a;3.3 GET vs POST&#xff1a; 4. HTTP的报头和状态码5. http的cookie5.1 htt…

估算总体标准差的极差均值估计法sigma = R/d2

总体标准差的估算值可以通过将平均极差除以合适的常数因子d2来计算。这个估算方法是用于估算总体标准差的一种常见方法&#xff0c;尤其在质量控制和过程监控中经常使用。 总体标准差的估算值 (平均极差) / d2 其中&#xff1a; "总体标准差的估算值" 表示用极差…

Floorplanning with Graph Attention

Floorplanning with Graph Attention DAC ’22 目录 Floorplanning with Graph Attention摘要1.简介2.相关工作3.问题公式化4. FLORA的方法4.1 解决方案概述4.2 C-谱聚类算法 4.3 基于GAT的模型4.4 合成训练数据集生成 摘要 布图规划一直是一个关键的物理设计任务&#xff0…

MFC与Qt常见窗体、控件对比

MFC常见窗体: MFC(Microsoft Foundation Classes)提供了一系列用于创建 Windows 图形用户界面(GUI)应用程序的常见窗体类。下面是一些常见的 MFC 窗体类: CDialog:对话框类,用于创建简单的对话框窗口,通常包含按钮、文本框、标签等控件。CFrameWnd:框架窗口类,用于…