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

【摘要】 ​今天,梳理下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");
    }

    @Override
    public 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{

    @Override
    public 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锁,如果是分布式环境下,应考虑使用分布式锁。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

路径规划 | 飞蛾扑火算法求解二维栅格路径规划(Matlab)

目录 效果一览基本介绍程序设计参考文献 效果一览 基本介绍 路径规划 | 飞蛾扑火算法求解二维栅格路径规划(Matlab)。 飞蛾扑火算法(Firefly Algorithm)是一种基于自然界萤火虫行为的优化算法,在路径规划问题中也可以应…

Nginx入门到精通三(反向代理1)

下面内容整理自bilibili-尚硅谷-Nginx青铜到王者视频教程 Nginx相关文章 Nginx入门到精通一(基本概念介绍)-CSDN博客 Nginx入门到精通二(安装配置)-CSDN博客 Nginx入门到精通三(Nginx实例1:反向代理&a…

子进程继承父进程文件描述符导致父进程打开设备文件失败

开发过程中有时会遇到需要在程序中执行三方程序或者shell脚本,一般会通过system(), popen(), exec簇来完成该功能。我们知道以上方法会通过fork创建子进程后在子进程中执行相应指令。如图1为某个示例流程,具体的程序执行流程如图2所示,线程my…

使用Python和MediaPipe实现手势控制音量(Win/Mac)

1. 依赖库介绍 OpenCV OpenCV(Open Source Computer Vision Library)是一个开源的计算机视觉和机器学习软件库。它包含了数百个计算机视觉算法。 MediaPipe MediaPipe是一个跨平台的机器学习解决方案库,可以用于实时人类姿势估计、手势识…

godis源码分析——database存储核心1

前言 redis的核心是数据的快速存储,下面就来分析一下godis的底层存储是如何实现,先分析单机服务。 此文采用抓大放小原则,先大的流程方向,再抓细节。 流程图 源码分析 现在以客户端连接,并发起set key val命令为例…

简单的SQL字符型注入

目录 注入类型 判断字段数 确定回显点 查找数据库名 查找数据库表名 查询字段名 获取想要的数据 以sqli-labs靶场上的简单SQL注入为例 注入类型 判断是数字类型还是字符类型 常见的闭合方式 ?id1、?id1"、?id1)、?id1")等,大多都是单引号…

微分方程的解法(Matlab)

微分方程分为刚性微分方程和非刚性微分方程,在数值解法中的表现和行为特性上存在显著差异。 刚性微分方程(Stiffness Equation)是指其数值分析的解只有在时间间隔很小时才会稳定,只要时间间隔略大,其解就会不稳定。这…

【BUG】Python3|COPY 指令合并 ts 文件为 mp4 文件时长不对(含三种可执行源代码和解决方法)

文章目录 前言源代码FFmpeg的安装1 下载2 安装 前言 参考: python 合并 ts 视频(三种方法)使用 FFmpeg 合并多个 ts 视频文件转为 mp4 格式 Windows 平台下,用 Python 合并 ts 文件为 mp4 文件常见的有三种方法: 调用…

项目范围管理-系统架构师(二十九)

1、(重点)软件设计包括了四个独立又相互联系的活动,高质量的()将改善程序结构的模块划分,降低过程复杂度。 A程序设计 B数据设计 C算法设计 D过程设计 解析: 软件设计包含四个,…

博客前端项目学习day01

这里写自定义目录标题 登录创建项目配置环境变量,方便使用登录页面验证码登陆表单 在VScode上写前端,采用vue3。 登录 创建项目 检查node版本 node -v 创建一个新的项目 npm init vitelatest blog-front-admin 中间会弹出询问是否要安装包&#xff0c…

R语言安装devtools包失败过程总结

R语言安装devtools包时,遇到usethis包总是安装失败,现总结如下方法,亲测可有效 一、usethis包及cli包安装问题 首先,Install.packages("usethis")出现如下错误,定位到是这个cli包出现问题 载入需要的程辑包…

Mac和VirtualBox Ubuntu共享文件夹

1、VirtualBox中点击设置->共享文件夹 2、设置共享文件夹路径和名称(重点来了:共享文件夹名称) 3、保存设置后重启虚拟机,执行下面的命令 sudo mkdir /mnt/share sudo mount -t vboxsf share /mnt/share/ 注:shar…

.快速幂.

按位与(Bitwise AND)是一种二进制运算,它逐位对两个数的二进制表示进行运算。对于每一位,只有两个相应的位都为1时,结果位才为1;否则,结果位为0。如:十进制9 & 5转化为二进制&am…

基于lstm的股票Volume预测

LSTM(Long Short-Term Memory)神经网络模型是一种特殊的循环神经网络(RNN),它在处理长期依赖关系方面表现出色,尤其适用于时间序列预测、自然语言处理(NLP)和语音识别等领域。以下是…

酒店管理系统小程序的设计

管理员账户功能包括:系统首页,个人中心,用户管理,酒店管理员管理,房间类型管理,房间信息管理,订单信息管理,系统管理 微信端账号功能包括:系统首页,房间信息…

智慧校园信息化大平台整体解决方案PPT(75页)

1. 教育信息化政策 教育部印发《教育信息化2.0行动计划》,六部门联合发布《关于推进教育新型基础设施建设构建高质量教育支撑体系的指导意见》,中共中央、国务院印发《中国教育现代化2035》。这些政策文件强调了教育的全面发展、面向人人、终身学习、因…

Linux vim文本编辑器

Vim(Vi IMproved)是一个高度可配置的文本编辑器,它是Vi编辑器的增强版本,广泛用于程序开发和系统管理。Vim不仅保留了Vi的所有功能,还增加了许多新特性,使其更加强大和灵活。 Vim操作模式 普通模式&#xf…

vue3.0 项目h5,pc端实现扫描二维码 qrcode-reader-vue3

qrcode-reader-vue3 插件简述 qrcode-reader-vue3插件,允许您在不离开浏览器的情况下检测和解码二维码。 🎥 访问设备摄像头并持续扫描传入帧。QrcodeStream🚮 渲染到一个空白区域,您可以在其中拖放要解码的图像。QrcodeDropZon…

大气热力学(8)——热力学图的应用之一

本篇文章源自我在 2021 年暑假自学大气物理相关知识时手写的笔记,现转化为电子版本以作存档。相较于手写笔记,电子版的部分内容有补充和修改。笔记内容大部分为公式的推导过程。 文章目录 8.1 复习斜 T-lnP 图上的几种线8.1.1 等温线和等压线8.1.2 干绝热…

LintCode 629 · 最小生成树【困难 并查集 Java】

题目 题目链接: https://www.lintcode.com/problem/629/ 思路 核心1:对connections进行排序,根据开销升序排序 核心2:并查集,合并集合,记录下合并的边缘 核心3:如果合并完后,集合数…