可重入锁深入学习(有码)

【摘要】 ​今天,梳理下java中的常用锁,但在搞清楚这些锁之前,先理解下 “临界区”。临界区在同步的程序设计中,临界区段活称为关键区块,指的是一个访问共享资源(例如:共享设备或是共享存储器)的程序片段,而这些共享资源又无法同时被多个线程访问的特性。---维基百科释义1:可重入互斥锁(维基百科的这个叫法更贴切),是互斥锁的一种,同一线程对其多次加锁不会产生死锁,可重入互斥锁也称为递归互斥锁或递归锁。 ...

今天,梳理下java中的常用锁,但在搞清楚这些锁之前,先理解下 “临界区”。

临界区

在同步的程序设计中,临界区段活称为关键区块,指的是一个访问共享资源(例如:共享设备或是共享存储器)的程序片段,而这些共享资源又无法同时被多个线程访问的特性。

---维基百科

释义1:可重入互斥锁(维基百科的这个叫法更贴切),是互斥锁的一种,同一线程对其多次加锁不会产生死锁,可重入互斥锁也称为递归互斥锁或递归锁。  ---维基百科

测试对象,等同于业务逻辑

package com.wangjianlong.algorithm.reentrantMutex;import lombok.extern.slf4j.Slf4j;@Slf4j
public class ObjectA implements Runnable{public synchronized void func1(){log.info("start func1");func2();log.info("end func1");}public synchronized void func2(){log.info("start func2");try {Thread.sleep(5000);} catch (InterruptedException e) {throw new RuntimeException(e);}log.info("end func2");}@Overridepublic void run() {func1();log.info("----------------------thread done--------------------------");}
}

执行测试

package com.wangjianlong.algorithm.reentrantMutex;public class Test {public static void main(String[] args) {ObjectA aObject = new ObjectA();new Thread(aObject, "testThread1").start();}
}

执行结果:

14:22:24.715 [testThread1] INFO com.wangjianlong.algorithm.reentrantMutex.ObjectA - start func1
14:22:24.718 [testThread1] INFO com.wangjianlong.algorithm.reentrantMutex.ObjectA - start func2
14:22:29.725 [testThread1] INFO com.wangjianlong.algorithm.reentrantMutex.ObjectA - end func2
14:22:29.725 [testThread1] INFO com.wangjianlong.algorithm.reentrantMutex.ObjectA - end func1
14:22:29.725 [testThread1] INFO com.wangjianlong.algorithm.reentrantMutex.ObjectA - ----------------------thread done--------------------------Process finished with exit code 0

我们可以看到,ObjectA的func1和func2均被synchronized修饰,但在func1里调用func2,并不会产生死锁,func2正常执行。

释义2:可重入互斥锁,当且仅当尝试加锁的线程就是持有该锁的线程时,加锁操作就会成功。可重入互斥锁一般会记录被加锁的次数,只有执行相同次数的解锁操作才会真正解锁。

我们对调用测试进行调整,由刚才的一个线程执行,修改为4个线程并发执行

package com.wangjianlong.algorithm.reentrantMutex;public class Test {public static void main(String[] args) {ObjectA aObject = new ObjectA();//同一个对象,启动四个线程执行new Thread(aObject, "testThread1").start();new Thread(aObject, "testThread2").start();new Thread(aObject, "testThread3").start();new Thread(aObject, "testThread4").start();}
}

看结果:

14:31:36.983 [testThread1] INFO com.wangjianlong.algorithm.reentrantMutex.ObjectA - start func1
14:31:36.986 [testThread1] INFO com.wangjianlong.algorithm.reentrantMutex.ObjectA - start func2
14:31:41.988 [testThread1] INFO com.wangjianlong.algorithm.reentrantMutex.ObjectA - end func2
14:31:41.988 [testThread1] INFO com.wangjianlong.algorithm.reentrantMutex.ObjectA - end func1
14:31:41.988 [testThread1] INFO com.wangjianlong.algorithm.reentrantMutex.ObjectA - ----------------------thread done--------------------------
14:31:41.988 [testThread4] INFO com.wangjianlong.algorithm.reentrantMutex.ObjectA - start func1
14:31:41.988 [testThread4] INFO com.wangjianlong.algorithm.reentrantMutex.ObjectA - start func2
14:31:46.992 [testThread4] INFO com.wangjianlong.algorithm.reentrantMutex.ObjectA - end func2
14:31:46.992 [testThread4] INFO com.wangjianlong.algorithm.reentrantMutex.ObjectA - end func1
14:31:46.992 [testThread4] INFO com.wangjianlong.algorithm.reentrantMutex.ObjectA - ----------------------thread done--------------------------
14:31:46.992 [testThread2] INFO com.wangjianlong.algorithm.reentrantMutex.ObjectA - start func1
14:31:46.992 [testThread2] INFO com.wangjianlong.algorithm.reentrantMutex.ObjectA - start func2
14:31:51.998 [testThread2] INFO com.wangjianlong.algorithm.reentrantMutex.ObjectA - end func2
14:31:51.998 [testThread2] INFO com.wangjianlong.algorithm.reentrantMutex.ObjectA - end func1
14:31:51.998 [testThread2] INFO com.wangjianlong.algorithm.reentrantMutex.ObjectA - ----------------------thread done--------------------------
14:31:51.998 [testThread3] INFO com.wangjianlong.algorithm.reentrantMutex.ObjectA - start func1
14:31:51.998 [testThread3] INFO com.wangjianlong.algorithm.reentrantMutex.ObjectA - start func2
14:31:57.007 [testThread3] INFO com.wangjianlong.algorithm.reentrantMutex.ObjectA - end func2
14:31:57.007 [testThread3] INFO com.wangjianlong.algorithm.reentrantMutex.ObjectA - end func1
14:31:57.007 [testThread3] INFO com.wangjianlong.algorithm.reentrantMutex.ObjectA - ----------------------thread done--------------------------Process finished with exit code 0

分析:每个线程我都设置了名称,我们对照输出日志,可以观察出,每个线程内部synchronized 锁体现了出了可重入,但线程之间是互斥的,后面的线程必须等前面的线程执行完毕后才能获得ObjectA的执行权。

释义3:可重入互斥锁,是指在要给线程中可以多次获取同一把锁,比如一个线程在执行一个带锁的方法,该方法中又调用了另外一个需要相同锁的方法,则该线程可以直接执行调用的方法(即可重入),而无需重新获得锁,而对于不同线程则相当于普通的互斥锁。java线程是基于“每线程(per-thread)”,而不是基于“每调用(per-invocation)”的,也就是说java为每个线程分配一个锁,而不是为每次调用分配一个锁

我们调整下代码,新建ObjectB,它和ObjectA的区别就是去掉了Runnable接口,而是使用单独的任务类Task1来调用,在Task1里我们进行的ObjectB的实例创建。

package com.wangjianlong.algorithm.reentrantMutex;import lombok.extern.slf4j.Slf4j;@Slf4j
public class ObjectB{public synchronized void func1(){log.info("start func1");func2();log.info("end func1");}public synchronized void func2(){log.info("start func2");try {Thread.sleep(5000);} catch (InterruptedException e) {throw new RuntimeException(e);}log.info("end func2");}
}

package com.wangjianlong.algorithm.reentrantMutex;import lombok.extern.slf4j.Slf4j;@Slf4j
public class Task1 implements Runnable{@Overridepublic void run() {//类的实例化不等于创建线程ObjectB objectB =new ObjectB();objectB.func1();log.info("------------------------------执行线程done!------------------------------");}
}

执行结果:

14:43:02.460 [testThread3] INFO com.wangjianlong.algorithm.reentrantMutex.ObjectB - start func1
14:43:02.463 [testThread3] INFO com.wangjianlong.algorithm.reentrantMutex.ObjectB - start func2
14:43:02.460 [testThread2] INFO com.wangjianlong.algorithm.reentrantMutex.ObjectB - start func1
14:43:02.460 [testThread1] INFO com.wangjianlong.algorithm.reentrantMutex.ObjectB - start func1
14:43:02.460 [testThread4] INFO com.wangjianlong.algorithm.reentrantMutex.ObjectB - start func1
14:43:02.464 [testThread2] INFO com.wangjianlong.algorithm.reentrantMutex.ObjectB - start func2
14:43:02.464 [testThread1] INFO com.wangjianlong.algorithm.reentrantMutex.ObjectB - start func2
14:43:02.464 [testThread4] INFO com.wangjianlong.algorithm.reentrantMutex.ObjectB - start func2
14:43:07.480 [testThread1] INFO com.wangjianlong.algorithm.reentrantMutex.ObjectB - end func2
14:43:07.480 [testThread1] INFO com.wangjianlong.algorithm.reentrantMutex.ObjectB - end func1
14:43:07.480 [testThread4] INFO com.wangjianlong.algorithm.reentrantMutex.ObjectB - end func2
14:43:07.480 [testThread3] INFO com.wangjianlong.algorithm.reentrantMutex.ObjectB - end func2
14:43:07.480 [testThread4] INFO com.wangjianlong.algorithm.reentrantMutex.ObjectB - end func1
14:43:07.480 [testThread1] INFO com.wangjianlong.algorithm.reentrantMutex.Task1 - ------------------------------执行线程done!------------------------------
14:43:07.480 [testThread3] INFO com.wangjianlong.algorithm.reentrantMutex.ObjectB - end func1
14:43:07.480 [testThread4] INFO com.wangjianlong.algorithm.reentrantMutex.Task1 - ------------------------------执行线程done!------------------------------
14:43:07.480 [testThread3] INFO com.wangjianlong.algorithm.reentrantMutex.Task1 - ------------------------------执行线程done!------------------------------
14:43:07.480 [testThread2] INFO com.wangjianlong.algorithm.reentrantMutex.ObjectB - end func2
14:43:07.480 [testThread2] INFO com.wangjianlong.algorithm.reentrantMutex.ObjectB - end func1
14:43:07.480 [testThread2] INFO com.wangjianlong.algorithm.reentrantMutex.Task1 - ------------------------------执行线程done!------------------------------Process finished with exit code 0

我们可以看到四个线程均立刻都得到了执行了,说明锁是与锁所在的类的实例相关的。

锁作为并发共享数据,保证一致性的工具。

java中的synchronized可重入锁,缺点是本线程有效,分布式就废了,如果用在数据库操作类应用存在缺陷。

操作DB的并发加锁应该考虑使用数据库乐观锁或者悲观锁;操纵内存的并发加锁应该考虑使用synchronized等Java锁,如果是分布式环境下,应考虑使用分布式锁。

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

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

相关文章

数据湖仓一体(二) 安装kafka

上传安装包到/opt/software目录并解压 [bigdatanode106 software]$ tar -zxvf kafka_2.12-3.5.2.tgz -C /opt/services/ 重命名kafka [bigdatanode106 software]$ mv kafka_2.12-3.5.2 kafka_3.5.2 配置环境变量 [bigdatanode106 ~]$ sudo vim /etc/profile.d/bigdata_…

深度学习调参

此文整理总结github上的一个资料,结尾附上链接。对于工程应用很有现实参考,带入实际工作场景中会有不少的收获。 这份文档旨在帮助工程师和研究人员系统性地优化深度学习模型的性能。它涵盖了从项目启动到模型部署的各个环节,包括&#xff1a…

6、evil box one

低—>中 目标:获取root权限以及2个flag 主机发现 靶机 192.168.1100.40 或者使用fping -gaq 192.168.100.1/24发现主机使用ping的方式。 端口扫描 发现开放了22和80 可以使用-A参数,-A参数会得到更多的扫描细节 访问80端口就是一个apache的基本的…

基于Python/MATLAB长时间序列遥感数据处理及在全球变化、植被物候提取、植被变绿与生态系统固碳分析、生物量估算与趋势分析应用

植被是陆地生态系统中最重要的组分之一,也是对气候变化最敏感的组分,其在全球变化过程中起着重要作用,能够指示自然环境中的大气、水、土壤等成分的变化,其年际和季节性变化可以作为地球气候变化的重要指标。此外,由于…

怎么安装Manim库在Windows环境下的Jupyter Notebook上

Manim 是解释性数学视频的动画引擎。 您可以使用它来制作数学视频(或其他字段)。也许你们会在有有些平台上会看过特别好看的数学动画,例如 3Blue1Brown等。这些动画特别好看,还特别丝滑,基本找不到太大的毛病。 我当初…

在 WebSocket 连接建立之前进行身份验证

要在 WebSocket 连接建立之前进行身份验证,可以采用以下常见方法: 基于 token 的鉴权:客户端在连接请求中携带 token,服务器端接收后验证 token 的合法性。例如,使用 ws 库时,可以在建立 WebSocket 连接时设置请求头: const socket = new WebSocket(ws://localhost:…

推荐 2 个 硬核的 AI 开源项目

01 AI 助手在你的终端中配对编程 Aider 由 Paul Gauthier 精心打造的开源AI配对编程工具,已经在GitHub上赢得了超过 12.8k 颗星星,人气爆棚! 这不仅仅是个工具,它是你在终端中的 AI 编程伙伴,帮你编辑存储在本地 Git 仓…

mavsdk_server安卓平台编译

1.下载好mavsdk并进入mavsdk目录 2.生成docker安卓平台文件 docker run --rm dockcross/android-arm64 >./dockcross-android-arm64 3.生成makefile ./dockcross-android-arm64 cmake -DCMAKE_BUILD_TYPERelease -DBUILD_MAVSDK_SERVERON -DBUILD_SHARED_LIBSOFF -Bbuild/…

JS进阶-异常处理

学习目标&#xff1a; 掌握异常处理 学习内容&#xff1a; throw抛异常try/catch捕获异常debugger throw抛异常&#xff1a; 异常处理是预估代码执行过程中可能发生的错误&#xff0c;然后最大程度的避免错误的发生导致整个程序无法继续运行。 <title>throw抛异常</…

用C在安卓手机上开发

在安卓手机上进行C语言开发需要一些特定的工具和设置。通常&#xff0c;C语言用于编写安卓的底层代码&#xff0c;如性能关键的模块或与硬件直接交互的部分。我们可以使用Android NDK&#xff08;Native Development Kit&#xff09;来开发这些部分。以下是如何在安卓手机上使用…

前端面试题-怎样获取 url 地址栏 ? 后面的查询字符串,并以键值对形式放到对象里面

哈喽小伙伴们大家好!今天继续更新面试题系列文章 以百度为例&#xff1a; 我们以百度搜索掘金&#xff0c;url 为以下格式 https://cn.bing.com/search?q%E7%A8%80%E5%9C%9F%E6%8E%98%E9%87%91&formANNTH1&refig668f422a37c343b6b0f4ac940f65d043&pcEDGENTP&am…

OceanBase 配置项系统变量实现及应用详解(2):系统变量的定义及使用场景

在上一篇博客&#xff0c;配置项的定义及使用方法&#xff0c;详细阐述了配置项的概念及其基本应用方式&#xff0c;这些配置项能够调控集群或租户的行为方式。然而&#xff0c;在实际使用OceanBase的过程中&#xff0c;我们有时仅希望针对当前会话调整某些行为特性&#xff0c…

免费的AI抠图工具 毫秒级抠图 离线可用 -鲜艺AI抠图

鲜艺AI抠图是一款免费的AI抠图工具&#xff0c;不登录、不联网&#xff0c;内嵌 AI 模型&#xff0c;快至毫秒级抠图&#xff0c;支持批量抠图&#xff0c;支持点击按钮选择图片、拖入图片、粘贴图片、粘贴图片链接、从网页拖入图片&#xff0c;支持Windows和macos&#xff0c;…

一次业务的批量数据任务的处理优化

文章目录 一次业务的批量数据任务的处理优化业务背景1.0版本 分批处理模式2.0版本 平衡任务队列模式3.0版本 优化调度平衡任务队列模式总结 一次业务的批量数据任务的处理优化 业务背景 一个重新生成所有客户的财务业务指标数据的批量数据处理任务。 1.0版本 分批处理模式 …

使用 NumPy 及其相关库(如 pandas、scikit-learn 等)时,由于 NumPy 的版本不兼容或者某些依赖库与 NumPy 的版本不匹配

题意&#xff1a; numpy.dtype size changed, may indicate binary incompatibility. Expected 96 from C header, got 88 from PyObject 问题背景&#xff1a; I want to call my Python module from the Matlab. I received the error: Error using numpy_ops>init thi…

Java中的List集合

一、ArrayLIst集合 ArrayList的特点 ArrayList实现了Collection接口ArrayList内部封装了一个Object类型的对象&#xff0c;初始长度为10&#xff0c;且长度可变ArrayList集合使用数组实现所以查询快&#xff0c;但是增删慢(因为需要移动元素)ArrayList是不能保证线程安全的 …

戴尔inspiron如何独显直连?

&#x1f3c6;本文收录于《CSDN问答解惑-专业版》专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收…

解决安卓tv 蓝牙遥控器配对后输入法弹不出来的问题

t972在蓝牙配对后&#xff0c;自带的LatinIME 输入法会出现弹不出来的现象。 经过分析&#xff0c;主要为蓝牙的kl 文件适配存在问题。解决如下&#xff1a; 1.新建 kl文件 这个需要结合选用的遥控器来设定名称&#xff0c;我这边的遥控器是按照如下配置的 Vendor_2b54_Pr…

java基础,接口和抽象类

一&#xff1a;接口和抽象类 ①接口的定义&#xff1a; 声明方式&#xff1a; 接口使用interface关键字来声明&#xff0c;后跟接口的名称和接口体&#xff08;包含常量和方法声明的代码块&#xff09; public interface ObjectService extends IService<ObjectDO> {/…

Vue 中 v-pre、v-once、v-cloak 标签的深度解析与案例展示

目录 v-pre v-once ​​​​​​​v-cloak ​​​​​​​v-cloak介绍 ​​​​​​​插值表达式闪烁问题 v-pre 当使用 v-pre 指令时,不会进行编译操作。所有的 Vue 模板语法都将得以完整保留,并会按照其初始的形态进行渲染。其中,最为常见的应用场景便是用于展示…