Java各种锁
目录
一、读写锁(ReentrantReadWriteLock)
二、非公平锁(synchronized/ReentrantLock)
三、可重入锁/递归锁(synchronized/ReentrantLock)
四、自旋锁(spinlock)
五、乐观锁/悲观锁
六、死锁
1、死锁代码
2、死锁的检测(jps -l 与 jstack 进程号)
七、sychronized-wait-notify 与 lock-await-signal的对比
1、sychronized与lock的对比
2、分组加锁的实例
本文通过学习:周阳老师-尚硅谷Java大厂面试题第二季 总结的锁相关的笔记
一、读写锁(ReentrantReadWriteLock)
| | |
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;class MyCache {private volatile Map<String, Object> map = new HashMap<>();//volatile保证可见性private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();public void put(String key, Object value) {rwLock.writeLock().lock();//写锁创建try {System.out.println(Thread.currentThread().getName() + "\t 正在写入:" + key);try {// 模拟网络拥堵,延迟0.3秒TimeUnit.MILLISECONDS.sleep(300);} catch (InterruptedException e) {e.printStackTrace();}map.put(key, value);System.out.println(Thread.currentThread().getName() + "\t 写入完成");} catch (Exception e) {e.printStackTrace();} finally {rwLock.writeLock().unlock();//写锁释放}}public void get(String key) {rwLock.readLock().lock();//读锁创建try {System.out.println(Thread.currentThread().getName() + "\t 正在读取:");try {TimeUnit.MILLISECONDS.sleep(300);//模拟网络拥堵,延迟0.3秒} catch (InterruptedException e) {e.printStackTrace();}Object value = map.get(key);System.out.println(Thread.currentThread().getName() + "\t 读取完成:" + value);} catch (Exception e) {e.printStackTrace();} finally {rwLock.readLock().unlock();//读锁释放}}public void clean() {map.clear();//清空缓存}
}public class ReadWriteLockDemo {public static void main(String[] args) {MyCache myCache = new MyCache();for (int i = 1; i <= 5; i++) {//5个线程写final int tempInt = i;//finalnew Thread(() -> {myCache.put(tempInt + "", tempInt + "");}, String.valueOf(i)).start();}for (int i = 1; i <= 5; i++) {//5个线程读final int tempInt = i;//finalnew Thread(() -> {myCache.get(tempInt + "");}, String.valueOf(i)).start();}}
}
二、非公平锁(synchronized/ReentrantLock)
定义 | 区别 | |
非公平锁 | 多个线程获取锁的顺序,并不是按照申请锁的顺序,有可能申请的线程比先申请的线程优先获取锁,在高并发环境下,有可能造成优先级翻转,或者饥饿的线程(也就是某个线程一直得不到锁) | 比较粗鲁,上来就直接尝试占有锁,如果尝试失败,就再采用类似公平锁那种方式 |
公平锁 | 多个线程按照申请锁的顺序来获取锁,类似于排队买饭,先来后到,先来先服务,就是公平的,也就是队列 | 很公平,在并发环境中,每个线程在获取锁时会先查看此锁维护的等待队列,如果为空,或者当前线程是等待队列中的第一个,就占用锁,否者就会加入到等待队列中,以后安装FIFO的规则从队列中取到自己 |
- synchronized是非公平锁
- ReentrantLock默认非公平锁
- Lock lock = new ReentrantLock(true);//默认false非公平锁,true公平锁
三、可重入锁/递归锁(synchronized/ReentrantLock)
synchronized可重入锁 | ReentrantLock可重入锁 |
class MySynchronized {public synchronized void sendSMS() throws Exception{//发短信System.out.println(Thread.currentThread().getName() + "\t invoked sendSMS()");sendEmail();//同步方法中调用另外一个同步方法}public synchronized void sendEmail() throws Exception{//发邮件System.out.println(Thread.currentThread().getId() + "\t invoked sendEmail()");}
}
public class MyDemo {public static void main(String[] args) {MySynchronized mySynchronized = new MySynchronized();new Thread(() -> {try {mySynchronized.sendSMS();} catch (Exception e) {e.printStackTrace();}}, "t1").start();new Thread(() -> {try {mySynchronized.sendSMS();} catch (Exception e) {e.printStackTrace();}}, "t2").start();}
}
/**
t1 invoked sendSMS()
t1 invoked sendEmail()
t2 invoked sendSMS()
t2 invoked sendEmail()
*/
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;class MyReentrantLock implements Runnable{Lock lock = new ReentrantLock();@Overridepublic void run() {method1();}public void method1() {lock.lock();try {System.out.println(Thread.currentThread().getName() + "\t exe method1");method2();} finally {lock.unlock();}}public void method2() {lock.lock();try {System.out.println(Thread.currentThread().getName() + "\t exe method2");} finally {lock.unlock();}}
}
public class ReenterLockDemo {public static void main(String[] args) {MyReentrantLock myReentrantLock = new MyReentrantLock();Thread t1 = new Thread(myReentrantLock, "t1");Thread t2 = new Thread(myReentrantLock, "t2");t1.start();t2.start();}
}
/**
t1 exe method1
t1 exe method2
t2 exe method1
t2 exe method2
*/
四、自旋锁(spinlock)
![]() | ![]() |
public class SpinLockDemo {// 现在的泛型装的是Thread,原子引用线程AtomicReference<Thread> atomicReference = new AtomicReference<>();public void myLock() {//加锁Thread thread = Thread.currentThread();System.out.println(Thread.currentThread().getName() + "\t come in ");//开始自旋,期望值是null,更新值是当前线程,如果是null,则更新为当前线程,否者自旋while(!atomicReference.compareAndSet(null, thread)) {}}public void myUnLock() {//解锁Thread thread = Thread.currentThread();//自己用完了后,把atomicReference变成nullatomicReference.compareAndSet(thread, null);System.out.println(Thread.currentThread().getName() + "\t invoked myUnlock()");}public static void main(String[] args) {SpinLockDemo spinLockDemo = new SpinLockDemo();new Thread(() -> {spinLockDemo.myLock();//加锁try {TimeUnit.SECONDS.sleep(5);} catch (InterruptedException e) {e.printStackTrace();}spinLockDemo.myUnLock();//释放锁}, "t1").start();//1秒后,启动t2线程占用锁try {TimeUnit.SECONDS.sleep(5);} catch (InterruptedException e) {e.printStackTrace();}new Thread(() -> {spinLockDemo.myLock();//加锁spinLockDemo.myUnLock();//释放锁}, "t2").start();}
}
/**
t1 come in
.....五秒后.....
t1 invoked myUnlock()
t2 come in
t2 invoked myUnlock()
*/
首先输出的是 t1 come in,然后1秒后,t2线程启动,发现锁被t1占有,然后不断执行compareAndSet方法,来进行比较,直到t1释放锁后,也就是5秒后,t2成功获取到锁,然后释放
五、乐观锁/悲观锁
1、MybatisPlus使用乐观锁的3步走
step1、在数据库增加version字段,默认为1
step2、在实体类增加对应的字段
@Version
private Integer version;
step3、注册乐观锁,在MybatisPlusConfig中配置
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
2、悲观锁
六、死锁
1、死锁代码
import java.util.concurrent.TimeUnit;class HoldLockThread implements Runnable{private String lockA;private String lockB;public HoldLockThread(String lockA, String lockB) {this.lockA = lockA;this.lockB = lockB;}@Overridepublic void run() {synchronized (lockA) {System.out.println(Thread.currentThread().getName() + "\t 自己持有" + lockA + "\t 尝试获取:" + lockB);try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}synchronized (lockB) {System.out.println(Thread.currentThread().getName() + "\t 自己持有" + lockB + "\t 尝试获取:" + lockA);}}}
}public class DeadLockDemo {public static void main(String[] args) {String lockA = "lockA";String lockB = "lockB";new Thread(new HoldLockThread(lockA, lockB), "t1").start();new Thread(new HoldLockThread(lockB, lockA), "t2").start();}
}
/**
t1 自己持有lockA 尝试获取:lockB
t2 自己持有lockB 尝试获取:lockA
*/
2、死锁的检测(jps -l 与 jstack 进程号)
step1、jps -l
step2、jstack 7560 #后面参数是jps输出的该类的pid
查看最后一行,我们看到 Found 1 deadlock,即存在一个死锁
七、sychronized-wait-notify 与 lock-await-signal的对比
sychronized - wait - notify |
lock - await - signal |
1、sychronized与lock的对比
sychronized | lock | |
1.定义 | JVM层面的java关键字,底层是通过monitor对象来完成 | api层面的锁,底层是JUC锁(java.util.concurrent.locks.Lock) |
2.使用方法 | 不需要用户去手动释放锁,系统自动释放 | 需要用户去手动释放锁,若没有主动释放锁,就有可能出现死锁的现象,需要lock() 和 unlock() 配置try catch语句来完成 |
3.等待是否中断 | 不可中断 | 可中断,可以设置超时方法
|
4.加锁是否公平 | 非公平锁 | 默认非公平锁,构造函数可以传递boolean值,true为公平锁,false为非公平锁 |
锁绑定多个条件Condition | 没有,要么随机,要么全部唤醒 | 可以精确唤醒 |
2、分组加锁的实例
题目:多线程之间按顺序调用,实现 A-> B -> C 三个线程启动,要求如下:
AA打印5次,BB打印10次,CC打印15次
紧接着
AA打印5次,BB打印10次,CC打印15次
..
来10轮
分析:链式唤醒的操作,适合用lock
![]() | ![]() |
class ShareResource {private int number = 1;//A=1,B=2,c=3private Lock lock = new ReentrantLock();//可重入锁// 这三个相当于备用钥匙private Condition condition1 = lock.newCondition();private Condition condition2 = lock.newCondition();private Condition condition3 = lock.newCondition();public void print5() {lock.lock();try {//step1、判断while(number != 1) condition1.await();//step2、干活for (int i = 0; i < 5; i++) {System.out.println(Thread.currentThread().getName() + "\t " + number + "\t" + i);}// step3、唤醒,通知B线程执行number = 2;condition2.signal();} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}public void print10() {lock.lock();try {//step1、判断while(number != 2) condition2.await();//step2、干活for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName() + "\t " + number + "\t" + i);}//step3、唤醒,通知C线程执行number = 3;condition3.signal();} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}public void print15() {lock.lock();try {//step1、判断while(number != 3) condition3.await();//step2、干活for (int i = 0; i < 15; i++) {System.out.println(Thread.currentThread().getName() + "\t " + number + "\t" + i);}//step3、唤醒,通知A线程执行number = 1;condition1.signal();} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}
}public class SyncAndReentrantLockDemo {public static void main(String[] args) {ShareResource shareResource = new ShareResource();new Thread(() -> {for (int i = 0; i < 10; i++) {shareResource.print5();}}, "A").start();new Thread(() -> {for (int i = 0; i < 10; i++) {shareResource.print10();}}, "B").start();new Thread(() -> {for (int i = 0; i < 10; i++) {shareResource.print15();}}, "C").start();}
}
相关文章:

Java各种锁
目录 一、读写锁(ReentrantReadWriteLock) 二、非公平锁(synchronized/ReentrantLock) 三、可重入锁/递归锁(synchronized/ReentrantLock) 四、自旋锁(spinlock) 五、乐观锁/悲观锁 六、死锁 1、死锁代码 2、死锁的检测(jps -l 与 jstack 进程号) 七、sychronized-wait…...

TryHackMe-Tardigrade(应急响应)
Tardigrade 您能否在此 Linux 端点中找到所有基本的持久性机制? 服务器已遭到入侵,安全团队已决定隔离计算机,直到对其进行彻底清理。事件响应团队的初步检查显示,有五个不同的后门。你的工作是在发出信号以使服务器恢复生产之前…...

导出GIS | 将EXCEL表格中坐标导出成GIS格式文件
一 前言 EXCEL是我们日常工作学习数据处理的办公软件,操作易上手,几乎人人都会用。EXCEL表格能够处理各种数据,包括经纬度坐标数据,地址数据等等。 有时因工作需要需将表格中地址数据处理为GIS格式的文件,以便能够将数…...

new set数组对象去重失败
我们知道Set是JS的一个种新的数据结构,和数组类似,和数组不同的是它可以去重,比如存入两个1或两个"123",只有1条数据会存入成功,但有个特殊情况,如果添加到set的值是引用类型,比如数组…...

Acwing: 一道关于线段树的好题(有助于全面理解线段树)
题目链接🔗:2643. 序列操作 - AcWing题库 前驱知识:需要理解线段树的结构和程序基本框架、以及懒标记的操作。 题目描述 题目分析 对区间在线进行修改和查询,一般就是用线段树来解决,观察到题目一共有五个操作&…...

DD-1/40 10-40mA型【接地继电器】
系列型号: DD-1/40接地继电器 DD-1/50接地继电器 DD-1/60接地继电器 一、 用途及工作原理 DD-1型接地继电器为瞬时动作的过电流继电器,用作小电流接地电力系统高电压三相交流发电机和电动机的接地零序过电流保护。继电器线圈接零序电流互感器(电缆式、母…...

【女神节】简单使用C/C++和Python嵌套for循环生成一个小爱心
目录 前言实现分析代码实现代码如下效果如下优化效果代码如下效果如下总结尾叙前言 女神节马上到了,有女朋友的小伙伴是不是已经精心准好礼物了呢!对于已婚男士,是不是整愁今天又该送什么礼物呢!说真的,我也整愁着,有什么要推荐么,评论留言下! 实现分析 可以先在纸上或…...

Biome-BGC生态系统模型与Python融合技术实践应用
查看原文>>> Biome-BGC生态系统模型与Python融合技术实践应用 Biome-BGC是利用站点描述数据、气象数据和植被生理生态参数,模拟日尺度碳、水和氮通量的有效模型,其研究的空间尺度可以从点尺度扩展到陆地生态系统。 在Biome-BGC模型中…...
ESP32 GPIO使用
ESP32 GPIO使用 #define GPIO_OUT_PIN 2 //定义引脚号 #define GPIO_OUTPUT_PIN_SEL (1<<GPIO_OUT_PIN) //定义输出引脚的宏,用来将输出引脚号转换为位掩码void bsp_gpio_init(){gpio_config_t io_conf;io_conf.pin_bit_mask GPIO_OUTPUT_PIN_SE…...

JavaScript 高级4 :正则表达式
JavaScript 高级4 :正则表达式 Date: January 19, 2023 Text: 正则表达式、正则表达式特殊字符、正则表达式中的替换 目标: 能够说出正则表达式的作用 能够写出简单的正则表达式 能够使用正则表达式对表单进行验证 能够使用正则表达式替换内容 正则…...

如何让AI帮你干活-娱乐(3)
背景今天的话题会偏代码技巧一些,对于以前没有接触过代码的朋友或者接触代码开发经验较少的朋友会有些吃力。上篇文章介绍了如何广视角的生成相对稳定的视频。昨天的实现相对简单,主要用的是UI界面来做生成。但是生成的效果其实也显而易见,不…...

webview的工作、内存泄漏、漏洞以及缓存机制原理原理+方案解决
分析一段appium的日志来分析webview的工作原理,文章尾部附有自动化脚本及完整日志: 解析: 获取上下文列表 服务端发送命令adb shell cat /proc/net/unix获取域套接字列表。那什么是域套接字呢? 域套接字:是unix系统里…...

BFD协议原理
BFD协议原理引入背景不使用BFD带来的问题OSPF感知慢VRRP产生次优路径BFD技术简介BFD会话建立方式和检测机制BFD会话建立过程BFD工作流程BFD的单臂回声BFD默认参数以及调整方法总结引入背景 随着网络应用的广泛部署,网络发生中断可能影响业务正常运行并造成重大损失…...

你把骑行当什么? 它就是你需要的
1.骑行是一种有活力的运动,尝试一下你一定会喜欢上它的!2.把骑行当作一种娱乐,让自己快乐地体验自然的美!3.骑行可以帮助你改变心态,让你的心情变得更加愉悦!4.让骑行成为你每天的计划,看看骑行…...
python基础系列 —— 迭代器与内置高阶函数
目录 一、迭代器 1、基本概念 2、如何定义一个迭代器 3、如果判断对象是否是迭代器 4、如何重置迭代器 5、如何调用迭代器 二、高阶函数 1、map函数 2、filter函数 3、reduce函数 4、sorted函数 一、迭代器 1、基本概念 迭代:是一个重复的过程,每次重复…...

MySQL面试题-日志
目录 1.MySQL 中常见的日志有哪些? 2.慢查询日志有什么用? 3.binlog 主要记录了什么? 4.Mysql的binlog有几种录入格式?分别有什么区别? 5.redo log 如何保证事务的持久性? 6.页修改之后为什么不直接刷…...
Android 10.0 去掉Launcher3默认给 icon增加的APK图标白边
1.概述 在10.0的系统产品开发中,Launcher3定制化开发中,发现在给第三方app的icon绘制图标的时候,会有白边第三方app的图标没有完全绘制出来,而系统app不存在这个问题,是完全绘制出来的,所以需要分析图标绘制类来解决这个问题 2.去掉Launcher3默认给 icon增加的APK图标白…...

E900V21C(S905L-armbian)安装armbian-Ubuntu(WiFi)
基本上是s905L芯片的刷机都是如此,包括Q7等 在网上寻找好多的教程关于e900v21c的刷机包和教程都少的可怜,唯一的就是这个:山东联通版创维E900V21C盒子刷入Armbiam并安装宝塔和Docker,但他是不能用WiFi和蓝牙的然后就是寻找s90l的…...
tpc协议的3次握手和4次挥手
建立连接的3次握手过程: A: 我想和你建立连接,你收到我的请求吗?(我想娶你) B: 好的,我收到了你的请求,我们可以建立连接,我同意。(好的,我愿意嫁给你) A: 好的,我收到了你的回应,我…...

YOLOv5害虫识别项目代码打包完整上传Gitee仓库(已开源)以及git上传速率限制踩坑记录
YOLOv5害虫识别项目代码打包完整上传Gitee仓库(已开源)以及git上传速率限制踩坑记录 ps: 最近很多小伙伴需要这个害虫识别项目的源码,由于文件过大,所以将代码完整上传至gitee,所有文件、教程、论文、以及代码模型…...
ES6从入门到精通:前言
ES6简介 ES6(ECMAScript 2015)是JavaScript语言的重大更新,引入了许多新特性,包括语法糖、新数据类型、模块化支持等,显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var…...

label-studio的使用教程(导入本地路径)
文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...
逻辑回归:给不确定性划界的分类大师
想象你是一名医生。面对患者的检查报告(肿瘤大小、血液指标),你需要做出一个**决定性判断**:恶性还是良性?这种“非黑即白”的抉择,正是**逻辑回归(Logistic Regression)** 的战场&a…...
PAN/FPN
import torch import torch.nn as nn import torch.nn.functional as F import mathclass LowResQueryHighResKVAttention(nn.Module):"""方案 1: 低分辨率特征 (Query) 查询高分辨率特征 (Key, Value).输出分辨率与低分辨率输入相同。"""def __…...

MySQL的pymysql操作
本章是MySQL的最后一章,MySQL到此完结,下一站Hadoop!!! 这章很简单,完整代码在最后,详细讲解之前python课程里面也有,感兴趣的可以往前找一下 一、查询操作 我们需要打开pycharm …...

水泥厂自动化升级利器:Devicenet转Modbus rtu协议转换网关
在水泥厂的生产流程中,工业自动化网关起着至关重要的作用,尤其是JH-DVN-RTU疆鸿智能Devicenet转Modbus rtu协议转换网关,为水泥厂实现高效生产与精准控制提供了有力支持。 水泥厂设备众多,其中不少设备采用Devicenet协议。Devicen…...
Windows 下端口占用排查与释放全攻略
Windows 下端口占用排查与释放全攻略 在开发和运维过程中,经常会遇到端口被占用的问题(如 8080、3306 等常用端口)。本文将详细介绍如何通过命令行和图形化界面快速定位并释放被占用的端口,帮助你高效解决此类问题。 一、准…...
「Java基本语法」变量的使用
变量定义 变量是程序中存储数据的容器,用于保存可变的数据值。在Java中,变量必须先声明后使用,声明时需指定变量的数据类型和变量名。 语法 数据类型 变量名 [ 初始值]; 示例:声明与初始化 public class VariableDemo {publi…...
精益数据分析(98/126):电商转化率优化与网站性能的底层逻辑
精益数据分析(98/126):电商转化率优化与网站性能的底层逻辑 在电子商务领域,转化率与网站性能是决定商业成败的核心指标。今天,我们将深入解析不同类型电商平台的转化率基准,探讨页面加载速度对用户行为的…...
无需布线的革命:电力载波技术赋能楼宇自控系统-亚川科技
无需布线的革命:电力载波技术赋能楼宇自控系统 在楼宇自动化领域,传统控制系统依赖复杂的专用通信线路,不仅施工成本高昂,后期维护和扩展也极为不便。电力载波技术(PLC)的突破性应用,彻底改变了…...