共享模型之管程(九)

news2024/11/25 10:10:21

1.ReentrantLock可重入锁

1.1.简介

1>.可重入是指同一个线程如果首次获得了这把锁,那么由于它是这把锁的拥有者,因此该线程有权利(/优先)再次获取这把锁;如果是不可重入锁,那么第二次获得锁时,自己也会被锁挡住;

ReentrantLock底层也是基于Monitor对象实现的,只不过它是在Java级别实现的(CAS+AQS),而synchronized是在JVM层面基于Monitor对象实现的线程安全/线程同步的机制,源码是C++的源码;

2>.ReentrantLock相对于Synchronized它具备如下特点:

①.可中断;
②.可以设置超时时间;
③.可以设置为公平锁(/先进先出,不会出现线程饥饿现象);
④.支持多个条件变量;

它与synchronized一样,都支持可重入!

1.2.基本语法

// 获取锁
ReentrantLock reentrantLock = new ReentrantLock(true);
// 加锁
reentrantLock.lock();
try {
  // 临界区
} finally {
  // 释放锁(这一步很重要!!!)
  reentrantLock.unlock();
}

Synchronized是在关键字这个级别保护临界区,而ReentrantLock是在对象的级别保护临界区,因此必须要先创建一个ReentrantLock对象;

1.3.可重入案例

@Slf4j
public class TestReentrantLockDemo1 {
    //定义ReentrantLock对象
    private static ReentrantLock reentrantLock = new ReentrantLock();

    public static void main(String[] args) {
        //加锁/获取锁,保护临界区
        reentrantLock.lock();
        try {
            log.info("进入mian方法");
            //当前线程获取到锁,但是没有释放,再去调用其他同步代码
            m1();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            log.info("main方法释放锁{}",reentrantLock);
            //解锁,会唤醒其他等待/阻塞中的线程
            //注意这个操作必须要和之前的lock()加锁操作成对出现!!!
            reentrantLock.unlock();
        }
    }

    private static void m1() {
        //加锁/获取锁,保护临界区
        reentrantLock.lock();
        try {
            log.info("进入m1方法");
            m2();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            log.info("m1方法释放锁{}",reentrantLock);
            //解锁,会唤醒其他等待/阻塞中的线程
            //注意这个操作必须要和之前的lock()加锁操作成对出现!!!
            reentrantLock.unlock();
        }
    }

    private static void m2() {
        //加锁/获取锁,保护临界区
        reentrantLock.lock();
        try {
            log.info("进入m2方法");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            log.info("m2方法释放锁{}",reentrantLock);
            //解锁,会唤醒其他等待/阻塞中的线程
            //注意这个操作必须要和之前的lock()加锁操作成对出现!!!
            reentrantLock.unlock();
        }
    }
}

在这里插入图片描述

1.4.可打断案例

@Slf4j
public class TestReentrantLockDemo2 {

    private static ReentrantLock reentrantLock = new ReentrantLock();

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            try {
                log.info("线程t1尝试获取锁...");
                //加锁并且指定当线程是可以被打断的
                //即如果当前没有其他线程竞争锁,那么当前线程正常获取到锁,相当于执行了lock()操作;如果有其他线程竞争锁,而当前当前线程没有抢到锁,那么当前线程就会进入到阻塞队列中等待,但是这个线程的等待状态是可以被其他线程用interrupt()方法打断/终止的!
                //可以避免线程无限制等待/阻塞,造成死锁!
                reentrantLock.lockInterruptibly();
            } catch (InterruptedException e) {
                log.info("没有获得锁,线程t1被打断,运行结束!");
                e.printStackTrace();
                return;
            }

            try {
                //获取到锁,执行业务代码
                log.info("线程t1获取到锁,执行业务代码...");
            } finally {
                log.info("线程t1释放锁");

                //解锁,会唤醒其他等待/阻塞中的线程
                reentrantLock.unlock();
            }
        }, "t1");


        //main线程获取锁
        reentrantLock.lock();
        try {
            log.info("main线程获取到锁...");
            t1.start();

            //main线程获取到锁,睡眠1s,但是没有释放锁,也就是说其他线程无法获取到当前这把锁而进入到阻塞队列中等待
            Thread.sleep(1000);

            //打断其他线程的等待状态
            t1.interrupt();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            log.info("main线程释放锁!");
            //解锁,会唤醒其他等待/阻塞中的线程
            reentrantLock.unlock();
        }
    }
}

在这里插入图片描述

1.5.锁超时案例

@Slf4j
public class TestReentrantLockDemo3 {
    //锁对象
    private static ReentrantLock reentrantLock = new ReentrantLock();

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            log.info("线程尝试获取锁...");
            //立刻返回线程获取锁的结果,不会等待!!!
            //if (!reentrantLock.tryLock()) {
            //    log.info("获取不到锁,执行结束!");
            //    return;
            //}

            try {
                //如果线程获取到锁,可以立刻返回结果!
                //如果线程没有获取到锁,它会等待一定的时间,在等待期间会不停尝试获取锁
                //如果等待期间提前获取到锁,那么提前结束等待,返回获取到锁的结果,即返回true;
                //如果等待时间到达仍然没有获取到锁,返回获取不到锁的结果,即返回false;
                //该方法也支持当前线程某种状态被打断!!
                if (!reentrantLock.tryLock(2, TimeUnit.SECONDS)){
                    log.info("获取不到锁,执行结束!");
                    return;
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
                log.info("没有获取到锁,线程被打断,执行结束!");
                return;
            }

            try {
                log.info("获取到锁,执行业务代码");
            } finally {
                log.info("线程t1执行完业务代码,释放锁!");
                reentrantLock.unlock();
            }
        }, "t1");

        //main线程获取锁
        log.info("main主线程尝试获取锁");
        reentrantLock.lock();
        try {
            log.info("main线程获取到锁,执行业务代码...");
        } finally {
            log.info("main线程执行完业务代码,释放锁!");
            reentrantLock.unlock();
        }

        t1.start();
        //打断t1线程的等待状态
        //t1.interrupt();
    }
}

在这里插入图片描述

1.6.公平锁

1>.ReentrantLock默认是不公平的,可以在创建ReentrantLock对象的时候在构造函数中指定一个"true"让它变成公平锁;(公平锁可以用来解决线程饥饿问题,但是一般没有必要,会降低并发度);
在这里插入图片描述
2>.Synchronized是非公平锁!

1.7.ReentrantLock条件变量

1.7.1.概述

1>.Synchronized中也有条件变量,就是之前的waitSet休息室,当获取锁的线程由于条件不满足时进入waitSet等待;

2>.ReentrantLock的条件变量类似Synchronized条件变量,但是它比synchronized强大之处在于,它是支持多个条件变量的,这就好比:

①.synchronized是那些不满足条件的线程都在一间休息室等消息;
②.而ReentrantLock支持多间休息室,有专门等烟的休息室、专门等早餐的休息室、唤醒时也是按相应的休息室来唤醒;

3>.条件变量API使用要点

①.await()执行前需要获得锁;
②.await()执行后会释放锁,进入某个conditionObject等待;
③.await()的线程被唤醒(或打断、或超时)取重新竞争lock锁;
④.竞争lock锁成功后,从await()方法后(/位置)继续执行;

1.7.2.案例

@Slf4j
public class TestConditionDemo1 {

    //锁对象
    static ReentrantLock reentrantLock = new ReentrantLock();
    //条件对象,一个锁对象可以对应多个条件对象,可以理解为休息室
    static Condition waitCigaretteSet = reentrantLock.newCondition();
    static Condition waitbreakfastSet = reentrantLock.newCondition();
    //共享变量
    static volatile boolean hasCigrette = false;
    static volatile boolean hasBreakfast = false;

    public static void main(String[] args) throws InterruptedException {
        new Thread(() -> {
            reentrantLock.lock();
            try {
                log.info("有烟没?{}", hasCigrette);
                while (!hasCigrette) {
                    log.info("没烟,先歇会...");
                    //获取锁的线程由于某些条件不满足,导致无法继续运行,而进入等待状态,可以理解为进入某个休息室内等待
                    //执行await(),会释放锁(唤醒其他处于阻塞状态的线程竞争锁)
                    //该方法可以让处于某种状态的线程被打断
                    waitCigaretteSet.await();
                }

                //正常干活
                log.info("有烟没?{}", hasCigrette);
                if (hasCigrette) {
                    log.info("有烟,可以开始干活...");
                } else {
                    log.info("没烟,没有干成活!");
                }

            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                reentrantLock.unlock();
            }
        }, "小南").start();

        new Thread(() -> {
            reentrantLock.lock();
            try {
                log.info("外卖到了没?{}", hasBreakfast);
                while (!hasBreakfast) {
                    log.info("外卖没到,先歇会...");
                    waitbreakfastSet.await();
                }

                log.info("外卖到了没?{}", hasBreakfast);
                if (hasBreakfast) {
                    log.info("外卖到了,可以干活...");
                } else {
                    log.info("外卖没到,没干成活!");
                }

            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                reentrantLock.unlock();
            }

        }, "小女").start();

        Thread.sleep(1000);
        new Thread(() -> {
            reentrantLock.lock();
            try {
                log.info("外卖到了...");
                hasBreakfast = true;
                //唤醒对应休息室里面的线程
                waitbreakfastSet.signal();
            } finally {
                reentrantLock.unlock();
            }
        }, "送外卖").start();

        Thread.sleep(1000);
        new Thread(() -> {
            reentrantLock.lock();
            try {
                log.info("烟到了...");
                hasCigrette = true;
                //唤醒对应休息室里面的线程
                waitCigaretteSet.signal();
            } finally {
                reentrantLock.unlock();
            }
        }, "送烟").start();
    }
}

在这里插入图片描述

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

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

相关文章

java支持的数据类型2023006

Java语言支持的类型分为两类:基本类型(Primitive Type)和引用类型(Reference Type)。 –基本类型包括boolean类型和数值类型。数值类型有整数类型和浮点类型。整数类型包括byte、short、int、long、char,浮…

dp(九)不同的子序列

不同的子序列_牛客题霸_牛客网【牛客题霸】收集各企业高频校招笔面试题目,配有官方题解,在线进行百度阿里腾讯网易等互联网名企笔试面试模拟考试练习,和牛人一起讨论经典试题,全面提升你的技术能力https://www.nowcoder.com/practice/ed2923e49d3d495f83…

Vue知识系列-axios

一、axios基础知识 axios是独立于vue的一个项目,基于promise用于浏览器和node.js的http客户端。 在浏览器中可以帮助我们完成ajax请求的发送在node.js中可以向远程接口发送请求 二、axios应用场景 三、axios的使用 第一步:创建html文件,在…

6.见过最强的后台管理系统

芋道,以开发者为中心,打造中国第一流的快速开发平台,全部开源,个人与企业可 100% 免费使用。 架构图 管理后台的 Vue3 版本采用 vue-element-plus-admin ,Vue2 版本采用 vue-element-admin 管理后台的移动端采用 uni-…

虹科方案 | 医药行业专用ECOLOG即插即用室内和设施环境监控系统

虹科ELPRO ECOLOG Plug&Play 连续监测解决方案采用功能强大的新ECOLOG-PRO硬件模块和ECOLOG-PRO Base服务器解决方案。即插即用是虹科ELPRO室内和设施环境监测解决方案增强型系列的一部分。ECOLOG即插即用是一种多功能,经济的系统,专为需要监测少于5…

转换流:代码与文件编码不一致读取乱码的问题

字符输入转换流:package com.gao;import java.io.*;public class transfer_stream {public static void main(String[] args) throws Exception {InputStream in new FileInputStream("E:\\黑马教育\\demo.txt");Reader re new InputStreamReader(in,&q…

Postman(07)Postman+OMySQL操作数据库

一、本文解决的问题 ● Postman断言需要查询数据存储情况; ● 部分业务需要从数据库中获取验证码(例如登录); ● 自动化场景需要执行数据正确性比对。 二、OMySQL连接数据库 1、OMySQL安装与启动 1.1 安装node.js 这个百度一…

【windows】VMware Workstation 不可恢复错误:(vmui) 错误代码0xc0000094

【问题描述】 打开需要启动的虚拟机后, 修改设备参数的时候,提示: VMware Workstation 不可恢复错误: (wmui) Exception 0xc0000094 has occurred。 【解决方法】 由于搭建的虚拟机环境一直在使用, 后来发现是VMware升级到17.0后…

【回眸】牛客网刷刷刷(三) Linux(续)软件工程(专题)

前言 近期的牛客网学习内容有Linux专题,以下记录一些笔试题中的Linux的知识点。 知识点 -u 用户号 指定用户的用户号;因为系统用户的用户号为 0,故指定用户号为 0; 如果同时有 -o 选项,则可以重复使用其他用户的标识…

马来酰亚胺-二硫键-活性酯;MAL-SS-NHS结构式

英 文 :MAL-SS-NHS 中文:马来酰亚胺-双硫键-活性酯 存储条件:-20C,避光,避湿 用 途:仅供科研实验使用,不用于诊治 外观: 固体或粘性液体,取决于分子量 注意事项:取用…

【机器学习】逻辑回归(理论)

逻辑回归(理论) 目录一、概论1、何为逻辑回归2、映射函数的引入3、伯努利分布二、损失函数的推导三、用逻辑回归实现多分类1、间接法:HardMax2、直接法:SoftMaxⅠ SoftMax的引入Ⅱ SoftMax的计算Ⅲ 引入SoftMax后的损失函数&#…

声网许振明:RTC 场景 UHD 视频应用和探索

大家好,我是声网的视频工程师许振明,今天跟大家主要介绍一下声网在 RTC 场景 UHD 视频的应用和探索。主要基于声网 HFR 和 VDR 两个系统来展开分享。 随着 RTC 技术的发展和应用,越来越多的场景都需要接入 RTC 的能力。尤其是随着编码技术、…

【Linux】Ubuntu20.04.5安装ROS1【教程】

文章目录【Linux】Ubuntu20.04.5安装ROS1【教程】写在前面,一些特别注意的点安装步骤大纲1.选择软件镜像源(可以省略,如果不成功再试这一步)2.添加ROS软件源和密钥3.安装ROS14.设置环境变量5.安装rosinstall6.检查安装是否成功Ref…

基于uds和modbus协议的bootloader对比

2022年参与了两个bootloader项目的开发,两个项目使用的芯片、通讯驱动不同,因此传输协议也不同,年底了将两个项目做一个对比,也顺便加深下对bootloader的理解。相同点:modbusuds刷新流程刷新流程大同小异,简…

活动星投票技能创意大赛网络评选微信的投票方式线上免费投票

“技能创意大赛”网络评选投票_建立投票链接_作品投票小程序_扫码投票制作方法现在来说,公司、企业、学校更多的想借助短视频推广自己。通过微信投票小程序,网友们就可以通过手机拍视频上传视频参加活动,而短视频微信投票评选活动既可以给用户…

C语言详解——自定义类型:结构体、枚举、联合体

🌇个人主页:平凡的小苏 📚学习格言:别人可以拷贝我的模式,但不能拷贝我不断往前的激情 🛸C语言专栏:https://blog.csdn.net/vhhhbb/category_12174730.html 小苏希望大家能从这篇文章中收获到许…

【Pytorch】稀疏矩阵的表示与加减运算

前提知识:LongTensor()是64位整数型向量,FloatTensor()是32位浮点数向量。 有大量0元素的时候,我们可以使用坐标形式存储稀疏矩阵。 一个3*3的矩阵,但是只有坐标(0,0)处有值(值为1)…

量子计算(二十二):Grover算法

文章目录 Grover算法 一、什么是搜索算法 二、怎么实现Grover搜索算法 Grover算法 一、什么是搜索算法 举一个简单的例子,在下班的高峰期,要从公司回到家里,开车走怎样的路线才能够耗时最短呢?最简单的想法,当然…

Radius vector (半径矢量)

Radius vector引言定义引言 今天给大家介绍一下什么是Radius vector。 定义 从圆心到当前位置的矢量r⃗\vec{r}r。它也被叫做位置向量。 其中,O表示圆心,这里我们称之为原点位置,A表示当前位置。因此矢量OA⃗\vec{OA}OA是一个Radius vec…

VsCode简单使用,配置c/c++编译环境,个人感受

整体感觉 本质就是一个加强的文本编译器,增加了终端和控制台,可以程序交互,相当于一个IDE的外部框架。和sublime有一定相似性,但界面和插件安装方面远远优于sublime。应该说这是一种为编译语言设计的显示框架,真正起作…