02_Lock锁

article/2025/8/25 23:28:03

首先看一下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();}}
}

http://chatgpt.dhexx.cn/article/G6ujro6D.shtml

相关文章

红米note10 pro机型解除“账号锁”的一些操作案例 mtk机型强解bl锁

前言。操作解除锁类案例只限于自己的机型&#xff0c; 因手机号长期不用或者忘记密码导致账号锁出现的问题 机型哦 我经常在csdn网站分享一些玩机中的实测资源和玩机常识。遇到很多玩机的友友&#xff0c;其中我分享的一个资源&#xff0c;这个粉丝朋友下载后在网吧操作不了。…

穷举法解华为bl锁

穷举法解华为bl锁 python3代码测试截图 灵感来自于&#xff1a;https://blog.csdn.net/qq_40169767/article/details/90481748 但是我不懂shell脚本&#xff0c;那个脚本又运行不了&#xff0c;所以我用python写了一个&#xff0c; 穷举要0.05s*9999999999999999/60*60*24*3651…

玩机搞机---关于安卓机型工厂固件 刷机 端口解密 解bl锁 写串 nv损坏 等相关常识

*******工程机和工厂固件方面的常识 可能很多玩机友友对什么是工厂固件比较陌生。那么今天的话题就围绕这个和大家讨论下。其实一般厂家的流程都是在一部机型推放市场之前&#xff0c;需要经过预研企划、研发设计、全面测试等诸多环节。在这一整个改善的全过程中&#xff0c;厂…

小米解锁BL锁(普通解锁)

关于BL锁&#xff0c;BootLoader锁&#xff0c;WIKI上的解释简单一点说就是 启动操作系统的软件&#xff0c;也就是引导程序&#xff0c;用于初始化内存等。 官方的说法是防止篡改程序所造成用户的经济和数据损失&#xff0c;网上的说法是可定义程度高&#xff0c;可卸载系统预…

Android系统刷机教程之解bl锁

由于安卓系统是基于linux开发的&#xff0c;属于嵌入式操作系统。在嵌入式操作系统中&#xff0c;BootLoader是在操作系统内核运行之前运行。可以初始化硬件设备、建立内存空间映射图&#xff0c;从而将系统的软硬件环境带到一个合适状态&#xff0c;以便为最终调用操作系统内核…

oppo r11 r11t解BL锁安装面具magisk详细教程

OPPO r11 r11t是高通骁龙660处理器&#xff0c;这个系列要解bl锁网上有很多教程比较http://rom点7to点cn/jiaochengdetail/16880 解好BL锁后就可以找三方rec&#xff0c;刷入rec 进入三方rec&#xff0c;把小包发送到手机硬盘&#xff0c;安装小包&#xff0c;再次重启进入三方…

华为手机一键解锁工具箱下载 | 华为手机解BL锁软件: 支持解锁bootloader,刷写recovery功能

文章目录 1. 软件介绍2. 特色功能3. 资源站点4. 下载地址5. 软件截图6. 安装教程7. 使用教程7.1. 解锁BL 1. 软件介绍 通过这款华为手机实用工具箱可以对你的华为手机系列进行刷机、解锁等操作&#xff0c;网上这种华为刷机解锁工具比较少&#xff0c;那么这款华为手机实用工具…

小米手机解bl锁正规方法!

申请解锁小米手机http://www.miui.com/unlock/download.html在手机上打开开发者模式&#xff0c;在开发者模式下-》设备解锁状态 然后关了wifi用手机卡上网绑定设备。 之后在菜单上选择清除所有数据&#xff0c;然后进入系统即可。

[HBZ分享] 小米手机如何解BL锁

第一步&#xff1a; 进入【设置—>我的设备–>全部参数–>连续疯狂的点MIUI版本那一行】 第二步&#xff1a;进入【更多设置–>开发者模式】&#xff0c;打开USB调试 与 USB安装 第三步&#xff1a;进入【更多设置–>开发者模式】&#xff0c;进入【设别解锁状…

9008 能 解锁BL_手机刷入面具及twrp教程(包含解bl锁教程)

原文作者:sgq694243467 原文链接:https://www.52pojie.cn/thread-1288591-1-1.html 最近了买黑鲨3pro 因为miui的广告很烦就想刷面具和xp框架屏蔽广告,但是网上似乎没什么教程,不是要收费就是空壳教程。经过一天测试和查找最终自己刷好了面具 root 和xp框架参考链接 https:…

【小白搞机入门】名词集-BootLoader锁(BL锁)

系列说明&#xff1a;由于作者认识浅薄&#xff0c;很多方面不能解释到十全十美&#xff0c;仅供参考。系列中收录的解决办法并非万能&#xff0c;请谨慎使用。 定义 BootLoader锁&#xff0c;以下简称“BL锁”&#xff0c;从字面意义上理解&#xff0c;是手机厂商对BootLoader…

XMind 8

XMind 8 Update 9 软件管理或官网下载XMind 8 Update 9 下载XMindCrack.jar 文件链接&#xff1a;https://pan.baidu.com/s/1qbI1sMFWBbaQrejI9VUFPQ 提取码&#xff1a;rgba 找到Xmind.ini文件&#xff0c;在Xmind.ini文件尾部追加“-javaagent:文件的绝对路径”&#xff08…

还在用PPT?XMind 8 Update 9 思维导图安装教程,赶紧收藏(亲测有效)

XMind 是一个全功能的思维导图和头脑风暴软件&#xff0c;为激发灵感和创意而生。作为一款有效提升工作和生活效率的生产力工具&#xff0c;受到全球百千万用户的青睐。 网上关于**的方法有很多&#xff0c;但好多都已经不能用了&#xff0c;所以我自己就整理了下&#xff0c;…

XMind 8 Update 9 升级 Pro 版

文章内所涉及的文件资料链接都将在文末提供。&#xff08;提供蓝奏云下载链接&#xff01;&#xff01;&#xff01;&#xff01;&#xff09; 可以登陆我的个人博客&#xff0c;查看更多教程&#xff0c;或者查找我的联系方式&#xff08;walker-king.cloud) 1、进入官网下载…

思维导图软件 XMind 8 和 XMind 2020的选择

时间&#xff1a;2020年4月21日 在网上搜索了下思维导图软件主要有 XMind&#xff0c;MindMaster和MindManager等 XMind是国内软件&#xff0c;有免费版 MindManager是国内软件&#xff0c;无免费版 XMind免费版基本可以满足需求&#xff0c;so选择了Xmind 在XMind官网可以…

Xmind 8思维导图使用方法

目录 一、作者有话说 二、如何下载Xmind8 三、Xmind 8傻瓜式教学 &#xff08;一&#xff09;创建主题 &#xff08;二&#xff09;创建分支 ​&#xff08;三&#xff09;删除分支 &#xff08;四&#xff09;添加内容 &#xff08;五&#xff09;格式修改 ​​&#…

【XMind】如何用XMind8绘制流程图?

XMind是一款用于绘制思维导图的软件&#xff0c;用于思维管理、协同办公、商业展示的辅助办公。其使用Java语言开发、开源、可跨平台运行。XMind软件绘制思维导图很容易上手&#xff0c;另外平衡图、树状图、逻辑图、矩阵等图也有绘制模板。可点击文件>新建选择合适的模板。…

Error running 'index.jsp': Address localhost:1099 is already in use

IDEA配置Tomcat并启动时出现如下错误&#xff1a; Error running ‘index.jsp’: Address localhost:1099 is already in use 错误原因是JMX的端口被已经被占用了。正如上图所示&#xff0c;Tomcat Service下开启了两个程序&#xff0c;他们的配置的JMX port一样。 JMX是什么&a…

解决运行tomcat 1099端口 Port already in use: 1099

cmd输入指令netstat -aon|findstr "1099"查询 tasklist|findstr "9212" taskkill /pid /9212 /f