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

02_Lock锁

首先看一下JUC的重磅武器——锁(Lock)

相比同步锁,JUC包中的Lock锁的功能更加强大,它提供了各种各样的锁(公平锁,非公平锁,共享锁,独占锁……),所以使用起来很灵活。

翻译过来就是:

锁实现提供了比使用同步方法和语句可以获得的更广泛的锁操作。它们允许更灵活的结构,可能具有非常不同的属性,并且可能支持多个关联的条件对象。

Lock是一个接口,这里主要有三个实现:

  • ReentrantLock
  • ReentrantReadWriteLock.ReadLock
  • ReentrantReadWriteLock.WriteLock

一、ReentrantLock可重入锁(递归锁) 

使用ReentrantLock改造卖票程序:只需改造sale()方法

private ReentrantLock lock = new ReentrantLock();  //创建实例对象

lock.lock(); //加锁

lock.unlock(); //释放锁

class Ticket{private Integer number = 20;private ReentrantLock lock = new ReentrantLock();public void sale(){lock.lock();if (number <= 0) {System.out.println("票已售罄!");lock.unlock();return;}try {Thread.sleep(200);number--;System.out.println(Thread.currentThread().getName() + "买票成功,当前剩余:" + number);} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}
}

1. 测试可重入性

可重入锁又名递归锁,是指在同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁。Java中ReentrantLock和synchronized都是可重入锁可重入锁的一个优点是可一定程度避免死锁。

例如下列伪代码:

class A{public synchronized void aa{......bb();......}public synchronized void bb{......}
}
A a = new A();
a.aa();

A类中有两个普通同步方法,都需要对象a的锁。如果是不可重入锁的话,aa方法首先获取到锁,aa方法在执行的过程中需要调用bb方法,此时锁被aa方法占有,bb方法无法获取到锁,这样就会导致bb方法无法执行,aa方法也无法执行,出现了死锁情况。可重入锁可避免这种死锁的发生。

class Ticket{private Integer number = 20;private ReentrantLock lock = new ReentrantLock();public void sale(){lock.lock();if (number <= 0) {System.out.println("票已售罄!");lock.unlock();return;}try {Thread.sleep(200);number--;System.out.println(Thread.currentThread().getName() + "买票成功,当前剩余:" + number);// 调用check方法测试锁的可重入性this.check();} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}/*** 为了测试可重入锁,添加检查余票方法*/public void check(){lock.lock();System.out.println("检查余票。。。。");lock.unlock();}
}

可以发现程序可以正常执行。。。说明该锁确实可重入。

AAA买票成功,当前剩余:19
检查余票。。。。
AAA买票成功,当前剩余:18
检查余票。。。。
AAA买票成功,当前剩余:17
检查余票。。。。
AAA买票成功,当前剩余:16
检查余票。。。。
AAA买票成功,当前剩余:15
检查余票。。。。
AAA买票成功,当前剩余:14
检查余票。。。。
AAA买票成功,当前剩余:13
检查余票。。。。
BBB买票成功,当前剩余:12
检查余票。。。。
BBB买票成功,当前剩余:11
检查余票。。。。
BBB买票成功,当前剩余:10
。。。。。。

2. 测试公平锁

ReentrantLock还可以实现公平锁。所谓公平锁,也就是在锁上等待时间最长的线程将获得锁的使用权。通俗的理解就是谁排队时间最长谁先执行获取锁。

ReentrantLock lock = new ReentrantLock();         //默认非公平锁
ReentrantLock lock = new ReentrantLock(true);     //true表示创建公平锁

    //默认非公平锁//ReentrantLock lock = new ReentrantLock();//true表示创建公平锁ReentrantLock lock = new ReentrantLock(true);public void test() throws InterruptedException {//lock.tryLock():获取锁并立即返货获取锁的结果,成功返回true,失败false
//        lock.tryLock(timeout,timeunit): 最多阻塞等待timeout单位timeunit 时间,获取成功返回true,失败falseif(lock.tryLock(6, TimeUnit.SECONDS)){//获取锁成功System.out.println(Thread.currentThread().getName()+"开始执行...");Thread.sleep(200);System.out.println(Thread.currentThread().getName()+"执行结束...");lock.unlock();}else{System.out.println(Thread.currentThread().getName()+"获取锁失败....");}}
}

测试结果:可以看到ABC三个线程是按顺序买票成功的。

AAA买票成功,当前剩余:19
检查余票。。。。
BBB买票成功,当前剩余:18
检查余票。。。。
CCC买票成功,当前剩余:17
检查余票。。。。
AAA买票成功,当前剩余:16
检查余票。。。。
BBB买票成功,当前剩余:15
检查余票。。。。
CCC买票成功,当前剩余:14
。。。。。。

3. 限时等待

这个是什么意思呢?也就是通过我们的tryLock方法来实现,可以选择传入时间参数,表示等待指定的时间,无参则表示立即返回锁申请的结果:true表示获取锁成功,false表示获取锁失败。我们可以将这种方法用来解决死锁问题

lock.tryLock():获取锁并立即返货获取锁的结果,成功返回true,失败false
lock.tryLock(timeout,timeunit): 最多阻塞等待timeout单位timeunit 时间,获取成功返回true,失败false
 

public class Demo3 {public static void main(String[] args) {Demo3 demo3 = new Demo3();new Thread(()->{demo3.test();},"A").start();new Thread(()->{demo3.test();},"B").start();}Lock lock = new ReentrantLock();public void test(){try {boolean b = lock.tryLock(1000, TimeUnit.MILLISECONDS);if(!b){System.out.println(Thread.currentThread().getName()+"获取锁失败");return;}Thread.sleep(2000);System.out.println(Thread.currentThread().getName()+"....");lock.unlock();} catch (InterruptedException e) {e.printStackTrace();lock.unlock();}}
}

4. ReentrantLock和synchronized区别

(1)synchronized是独占锁,加锁和解锁的过程自动进行,易于操作,但不够灵活。ReentrantLock也是独占锁,加锁和解锁的过程需要手动进行,不易操作,但非常灵活。

(2)synchronized可重入,因为加锁和解锁自动进行,不必担心最后是否释放锁;ReentrantLock也可重入,但加锁和解锁需要手动进行,且次数需一样,否则其他线程无法获得锁。

(3)synchronized不可响应中断,一个线程获取不到锁就一直等着;ReentrantLock可以响应中断。

(4)synchronzied锁的是对象,锁是保存在对象头里面的,根据对象头数据来标识是否有线程获得锁/争抢锁;ReentrantLock锁的是线程,根据进入的线程和int类型的state标识锁的获得/争抢。

二、ReentrantReadWriteLock读写锁

在并发场景中用于解决线程安全的问题,我们几乎会高频率的使用到独占式锁,通常使用java提供的关键字synchronized或者concurrents包中实现了Lock接口的ReentrantLock。它们都是独占式获取锁,也就是在同一时刻只有一个线程能够获取锁

现实中有这样一种场景:对共享资源有读和写的操作,且写操作没有读操作那么频繁。在没有写操作的时候,多个线程同时读一个资源没有任何问题,所以应该允许多个线程同时读取共享资源;但是如果一个线程想去写这些共享资源,就不应该允许其他线程对该资源进行读和写的操作了。大部分只是读数据,写数据很少,如果仅仅是读数据的话并不会影响数据正确性(出现脏读),而如果在这种业务场景下,依然使用独占锁的话,很显然这将是出现性能瓶颈的地方。针对这种读多写少的情况,java还提供了另外一个实现Lock接口的ReentrantReadWriteLock(读写锁)。读写锁允许同一时刻被多个读线程访问,但是在写线程访问时,所有的读线程和其他的写线程都会被阻塞。

接下来以缓存为例用代码演示读写锁,重现问题:

class MyCache{private volatile Map<String, String> cache= new HashMap<>();public void put(String key, String value){try {System.out.println(Thread.currentThread().getName() + " 开始写入!");Thread.sleep(300);cache.put(key, value);System.out.println(Thread.currentThread().getName() + " 写入成功!");} catch (InterruptedException e) {e.printStackTrace();} finally {}}public void get(String key){try {System.out.println(Thread.currentThread().getName() + " 开始读出!");Thread.sleep(300);String value = cache.get(key);System.out.println(Thread.currentThread().getName() + " 读出成功!" + value);} catch (InterruptedException e) {e.printStackTrace();} finally {}}
}public class ReentrantReadWriteLockDemo {public static void main(String[] args) {MyCache cache = new MyCache();for (int i = 1; i <= 5; i++) {String num = String.valueOf(i);// 开启5个写线程new Thread(()->{cache.put(num, num);}, num).start();}for (int i = 1; i <= 5; i++) {String num = String.valueOf(i);// 开启5个读线程new Thread(()->{cache.get(num);}, num).start();}}
}

打印结果:多执行几次,有很大概率不会出现问题

改造MyCache,加入读写锁

class MyCache{private volatile Map<String, String> cache= new HashMap<>();// 加入读写锁ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();public void put(String key, String value){// 加写锁rwl.writeLock().lock();try {System.out.println(Thread.currentThread().getName() + " 开始写入!");Thread.sleep(500);cache.put(key, value);System.out.println(Thread.currentThread().getName() + " 写入成功!");} catch (InterruptedException e) {e.printStackTrace();} finally {// 释放写锁rwl.writeLock().unlock();}}public void get(String key){// 加入读锁rwl.readLock().lock();try {System.out.println(Thread.currentThread().getName() + " 开始读出!");Thread.sleep(500);String value = cache.get(key);System.out.println(Thread.currentThread().getName() + " 读出成功!" + value);} catch (InterruptedException e) {e.printStackTrace();} finally {// 释放读锁rwl.readLock().unlock();}}
}

相关文章:

02_Lock锁

首先看一下JUC的重磅武器——锁&#xff08;Lock&#xff09; 相比同步锁&#xff0c;JUC包中的Lock锁的功能更加强大&#xff0c;它提供了各种各样的锁&#xff08;公平锁&#xff0c;非公平锁&#xff0c;共享锁&#xff0c;独占锁……&#xff09;&#xff0c;所以使用起来…...

面试总结,4年经验

小伙伴你好&#xff0c;我是田哥。 本文内容是一位星球朋友昨天面试遇到的问题&#xff0c;我把核心的问题整理出来了。 1&#xff1a;Java 层面的锁有用过吗&#xff1f;除了分布式锁以外 是的&#xff0c;Java中提供了多种锁机制来保证并发访问数据的安全性和一致性。常见的J…...

享受简单上传体验:将Maven仓库迁移到GitHub

前言&#xff1a;我为什么放弃了Maven Central 之前我写过一篇《Android手把手&#xff0c;发布开源组件至 MavenCentral仓库》&#xff0c;文中详细介绍了如何发布组件到Maven Central中供所有开发者共用。但是最近使用下来&#xff0c;发现Sonatype JIRA 的Maven Center上传…...

R语言 | 进阶字符串的处理

目录 一、语句的分割 二、修改字符串的大小写 三、unique()函数的使用 四、字符串的连接 4.1 使用paste()函数常见的失败案例1 4.2 使用paste()函数常见的失败案例2 4.3 字符串的成功连接与collapse参数 4.4 再谈paste()函数 4.5 扑克牌向量有趣的应用 五、字符串数据的…...

【MySQL高级】——InnoDB索引MyISAM索引

一、索引概述 MySQL官方对索引的定义为&#xff1a;索引&#xff08;Index&#xff09;是帮助MySQL高效获取数据的数据结构。 索引的本质&#xff1a;索引是数据结构。你可以简单理解为“排好序的快速查找数据结构”&#xff0c;满足特定查找算法。 这些数据结构以某种方式指向…...

电影《灌篮高手》观后

上周和同学一起看了电影《灌篮高手》这部电影&#xff0c;个人以前没有看过相关漫画和动画&#xff0c;但记得&#xff0c;看过海报和一些宣传物品&#xff0c;有的衣服上&#xff0c;有文具盒上&#xff0c;也都出现过&#xff0c;而且是在自己小时候&#xff0c;可见当时的影…...

C# .Net 中的同步上下文

.Net 中的同步上下文 【文 / 张赐荣】 什么是同步上下文&#xff1f; 同步上下文&#xff08;SynchronizationContext&#xff09;是一个抽象类&#xff0c;它提供了一个基本的功能&#xff0c;用于在不同的同步模型中传播一个同步操作。 同步上下文表示一个代码执行的位置&a…...

3分钟入门:Flex 布局

flex 布局原理 全称 flexible box&#xff0c;弹性布局。 如何开启&#xff1a;为元素添加 display: flex。 开启 flex 布局的元素&#xff0c;称为 flex 容器&#xff08;flex container&#xff09;&#xff0c;其子元素成为容器成员&#xff0c;称为 flex 项目。 flex 布…...

我想知道,就目前形势而言,学java好还是C++好?

前言 就现实点看看&#xff0c;可以对比现在Java和C的市场占有率&#xff0c;可以看到&#xff0c;到目前为止&#xff0c;Java在国内编程语言的市场仍然是占据着大头&#xff0c;在招聘当中Java的人数占有率仍然是遥遥领先于C&#xff0c;Java目前开阔的市场以及其巨大的岗位…...

Mysql 管理

目录 0 课程视频 1 系统数据库 -> 安装完mysql ->自带四个数据库 2 常用工具 -> 写脚本用 2.1 mysql 客户端工具 2.2 mysqladmin 2.3 mysqlbinlog -> 二进制日志 -> 运维讲解 2.4 mysqlshow 2.5 mysqldump 备份用 ->导出 2.6 mysqlimport/source -…...

C#基础(算术运算符)

作用 算术运算符 是用于 数值类型变量计算的运算符 它的返回结果是数值 赋值符号 // // 关键知识点&#xff1a; // 先看右侧 再看左侧 把右侧的值赋值给左侧的值 int myAge 18; 算术运算符 加 // 用自己计算 先算右侧结果 在赋值给左侧变量 int i 1; i i 2; …...

BM43-包含min函数的栈

题目 定义栈的数据结构&#xff0c;请在该类型中实现一个能够得到栈中所含最小元素的 min 函数&#xff0c;输入操作时保证 pop、top 和 min 函数操作时&#xff0c;栈中一定有元素。 此栈包含的方法有&#xff1a; push(value):将value压入栈中pop():弹出栈顶元素top():获取…...

[学习笔记] [机器学习] 3. KNN( K-近邻算法)及练习案例

视频链接数据集下载地址&#xff1a;《3. KNN及练习案例》配套数据集 1. K-近邻算法(KNN)概念 学习目标&#xff1a; 掌握K-近邻算法实现过程知道K-近邻算法的距离公式知道K-近邻算法的超参数 K K K值以及取值问题知道kd树实现搜索的过程应用KNeighborsClassifier实现分类知…...

React Hooks 钩子函数错误用法,你还在犯这些错误吗

React Hooks 常见错误 前言 本片文章主要是在写react hooks的时候&#xff0c;遇到的常见错误的写法&#xff0c;和错误。也是一个对只是的巩固和总结。 错误一 上代码&#xff1a;正确写法 function TestReactHooksError() {const [test, setTest] useState(test);useEff…...

tpm2-tools源码分析之tpm2_evictcontrol.c(1)

TPM 2.0中的tpm2_evictcontrol命令对应的源文件就是tpm2_evictcontrol.c&#xff0c;该文件位于tpm2-tools/tools/下&#xff0c;一共有339行&#xff08;版本5.5&#xff09;。 tpm2_evictcontrol的功能是使一个被加载的密钥持久保存、或者从TPM中移除一个持久密钥。命令描述…...

SpringCloud_OpenFeign服务调用和Resilience4J断路器

文章目录 一、负载均衡概论1、服务器负载均衡2、客户端负载均衡3、客户端负载均衡策略(SpringCloudRibbon)4、客户端负载均衡策略(SpringCloudLoadBalancer) 二、SpringCloudOpenFeign服务调用1、OpenFeign服务调用的使用2、OpenFeign服务调用的日志增强3、OpenFeign服务调用超…...

【C++】switch 语句

目录 1、缘起 2、笔记整理 3、if 和 switch 区别 4、总结 1、缘起 最近&#xff08;2023-04-29&#xff09;在 BiliBili 黑马程序员学习 C 编程语言&#xff0c;今天学习到了 switch 语句。以前在学习 C 语言 的时候&#xff0c;对这块知识点掌握的不是很好&#xff0c;…...

【Database-06】Centos 9 安装docker版的Oceanbase

1、安装docker 1.1、卸载旧版本 旧版本的 Docker 被称为docker或docker-engine。如果安装了这些&#xff0c;卸载它们以及相关的依赖项。 yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotat…...

TiDB Operator 和 Operator Dashboard

TiDB Operator 和 Operator Dashboard V1TiDB Operator概念实现 Operator Dashboard概念实现 V2思路实例代码TiDB ARM OperatorTiDB ARM Operator Dashboard V1 为了演示如何编写 TiDB Operator 和 Operator Dashboard&#xff0c;我们将分别介绍它们的概念和实现。 TiDB Ope…...

计算机网络闲谈01——QUIC协议

计算机网络闲谈01——QUIC协议 预备知识 重传机制 RTT 一个连接的往返时间 RTO 重传超时时间 RTT和RTO 的关系是&#xff1a;由于网络波动的不确定性&#xff0c;每个RTT都是动态变化的&#xff0c;所以RTO也应随着RTT动态变化。 流量控制 对发送方发送速率的控制 称之为…...

调用支付宝接口响应40004 SYSTEM_ERROR问题排查

在对接支付宝API的时候&#xff0c;遇到了一些问题&#xff0c;记录一下排查过程。 Body:{"datadigital_fincloud_generalsaas_face_certify_initialize_response":{"msg":"Business Failed","code":"40004","sub_msg…...

黑马Mybatis

Mybatis 表现层&#xff1a;页面展示 业务层&#xff1a;逻辑处理 持久层&#xff1a;持久数据化保存 在这里插入图片描述 Mybatis快速入门 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/6501c2109c4442118ceb6014725e48e4.png //logback.xml <?xml ver…...

【Linux】C语言执行shell指令

在C语言中执行Shell指令 在C语言中&#xff0c;有几种方法可以执行Shell指令&#xff1a; 1. 使用system()函数 这是最简单的方法&#xff0c;包含在stdlib.h头文件中&#xff1a; #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...

Java如何权衡是使用无序的数组还是有序的数组

在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...

(二)TensorRT-LLM | 模型导出(v0.20.0rc3)

0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述&#xff0c;后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作&#xff0c;其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...

HTML 列表、表格、表单

1 列表标签 作用&#xff1a;布局内容排列整齐的区域 列表分类&#xff1a;无序列表、有序列表、定义列表。 例如&#xff1a; 1.1 无序列表 标签&#xff1a;ul 嵌套 li&#xff0c;ul是无序列表&#xff0c;li是列表条目。 注意事项&#xff1a; ul 标签里面只能包裹 li…...

镜像里切换为普通用户

如果你登录远程虚拟机默认就是 root 用户&#xff0c;但你不希望用 root 权限运行 ns-3&#xff08;这是对的&#xff0c;ns3 工具会拒绝 root&#xff09;&#xff0c;你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案&#xff1a;创建非 roo…...

MODBUS TCP转CANopen 技术赋能高效协同作业

在现代工业自动化领域&#xff0c;MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步&#xff0c;这两种通讯协议也正在被逐步融合&#xff0c;形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...

DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI

前一阵子在百度 AI 开发者大会上&#xff0c;看到基于小智 AI DIY 玩具的演示&#xff0c;感觉有点意思&#xff0c;想着自己也来试试。 如果只是想烧录现成的固件&#xff0c;乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外&#xff0c;还提供了基于网页版的 ESP LA…...

MySQL 8.0 OCP 英文题库解析(十三)

Oracle 为庆祝 MySQL 30 周年&#xff0c;截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始&#xff0c;将英文题库免费公布出来&#xff0c;并进行解析&#xff0c;帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...