ReentranLock(可重入锁)

news2025/7/19 11:27:04

一、ReentranLock

ReentranLock属于JUC并发工具包下的类,相当于 synchronized具备如下特点

● 可中断
● 可以设置超时时间
● 可以设置为公平锁(防止线程出现饥饿的情况)
● 支持多个条件变量

与 synchronized一样,都支持可重入

基本语法(synchronized在关键字级别保护临界区, reentrantLock是在对象的级别来保护临界区)

// 获取锁
reentrantLock.lock();
try {
 // 临界区
} finally {
 // 释放锁(无论是否出现异常,均会将锁释放)
 reentrantLock.unlock();
}

lock()与unlock()是成对出现的

1.1 可重入

可重入是指同一个线程对象如果首次获得这把锁,那么因为它是这把锁的拥有者,因此有权利再次获取这把锁
如果是不可重入锁,那么第二次获得锁时,自身也会被锁挡住

import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.locks.ReentrantLock;

@Slf4j(topic = "c.Test")
public class Test {

    // 创建锁重入对象
    private static ReentrantLock lock = new ReentrantLock();
    
    public static void main(String[] args) throws InterruptedException {
        
        // 加锁
        lock.lock();
        try {
            log.debug("enter  main");
            m1();
        } finally {
            // 解锁
            lock.unlock();
        }
    }
    
    public static void m1() {
        // 加锁
        lock.lock();
        try {
            log.debug("enter  m1");
            m2();
        } finally {
            // 解锁
            lock.unlock();
        }
    }
    
    public static void m2() {
        // 加锁
        lock.lock();
        try {
            log.debug("enter  m2");
        } finally {
            // 解锁
            lock.unlock();
        }
    }
}

运行结果:(锁重入成功)

在这里插入图片描述

1.2 可打断——lockInterruptibly

在等待锁的过程中其他线程可以用interruput()方法终止等待

import cn.itcast.n2.util.Sleeper;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.locks.ReentrantLock;

@Slf4j(topic = "c.Test")
public class Test {

    // 创建锁重入对象
    private static ReentrantLock lock = new ReentrantLock();

    public static void main(String[] args)  {

         Thread t1=new Thread(()->{
            try {
                // 尝试获取锁,但可以被打断(如果没有别的线程竞争锁,此方法就会获取lock对象上的锁)
                /*若有竞争进入阻塞队列等待*/
                log.debug("尝试获得锁");
                lock.lockInterruptibly();
            } catch (InterruptedException e) {
                e.printStackTrace();
                log.debug("没有获取锁,返回");
                return;

            }try {
                log.debug("获取到锁");
            }finally {
                // 将锁释放掉
                lock.unlock();
            }
        },"t1");

      // 主线程先对其进行加锁后,t1线程才启动
      lock.lock();
      t1.start();
      // 主线程睡眠1s后打断t1
      Sleeper.sleep(1);
      t1.interrupt();
    }
}

运行结果:(成功打断t1线程)

1.3 锁超时

锁超时:在获取锁的过程中,如果其他线程持有锁一直未释放,去尝试获取锁的线程也不会死等,而是等待一段时间,若这段时间超过对方仍未释放锁,则放弃等待,获取锁失败

可打断属于一种被动的避免无限等待(死等)方式;而锁超时以主动的方式避免死等

1、无其他线程竞争锁:

import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.locks.ReentrantLock;

@Slf4j(topic = "c.Test")
public class Test {

    // 创建锁重入对象
    private static ReentrantLock lock = new ReentrantLock();

    public static void main(String[] args) {

        Thread t1 = new Thread(() -> {
            log.debug("尝试获得锁");
            // 尝试获取锁,返回值为布尔型  【成功:获取锁   失败:不可获得锁,不会进入阻塞队列等待】
            if (!lock.tryLock()) {           //失败则立刻返回(没有任何等待时间)
                log.debug("获取锁失败");    // false
                return;
            }
            try {
                // 执行临界区代码
                log.debug("成功获取锁");
            } finally {
                lock.unlock();     // 释放锁
            }

        });
    }
}

运行结果:
在这里插入图片描述

2、存在其他线程竞争(立刻结束):

import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.locks.ReentrantLock;

@Slf4j(topic = "c.Test")
public class Test {

    // 创建锁重入对象
    private static ReentrantLock lock = new ReentrantLock();

    public static void main(String[] args) {

        Thread t1 = new Thread(() -> {
            log.debug("尝试获得锁");
            // 尝试获取锁,返回值为布尔型  【成功:获取锁   失败:不可获得锁,不会进入阻塞队列等待】
            if (!lock.tryLock()) {
                log.debug("获取锁失败");    // false
                return;
            }
            try {
                // 执行临界区代码
                log.debug("成功获取锁");
            } finally {
                lock.unlock();     // 释放锁
            }
        });

        // 主线程先对lock对象加锁
       lock.lock();
        log.debug("成功获取锁");
        t1.start();
    }
}

运行结果:
在这里插入图片描述

3、存在其他线程竞争(等待一段时间):尝试等待1s,1s内若主线程还未释放锁再结束
在这里插入图片描述

哲学家就餐问题便可以使用tryLock()解决

1.4 公平锁

ReentrantLock 默认是不公平的。当一个线程持有锁,其他线程就会进入阻塞队列等待,当锁的持有者释放锁时,阻塞队列中等待的线程会一拥而上,谁先争抢到锁谁便是Owner,而不会按进入阻塞队列的先后顺序先来先得

(通过查看源码发现其构造方法中有一个带boolean类型参数的方法,其参数fair默认为false,可以修改其布尔值保证其公平性)公平锁一般没有必要,会降低并发度

二、ReentranLock条件变量

2.1 简介

条件变量

synchronized 中也有条件变量,就是我们讲原理时那个 waitSet 休息室,当条件不满足时进入 waitSet 等待

ReentrantLock 的条件变量比 synchronized 强大之处在于,它是支持多个条件变量的,这就好比

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

使用要点:
● await 前需要获得锁
● await 执行后,会释放锁,进入 conditionObject 等待
● await 的线程被唤醒(或打断、或超时)取重新竞争 lock 锁
● 竞争 lock 锁成功后,从 await 后继续执行

使用例子:

import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import static cn.itcast.n2.util.Sleeper.sleep;

@Slf4j(topic = "c.Test24")
public class Test24 {

    static final Object room = new Object();

    static boolean hasCigarette = false;
    static boolean hasTakeout = false;

    static ReentrantLock ROOM = new ReentrantLock();
    // 等待烟的休息室(创建一个新的条件变量)
    static Condition waitCigaretteSet = ROOM.newCondition();
    // 等外卖的休息室(创建一个新的条件变量)
    static Condition waitTakeoutSet = ROOM.newCondition();

    public static void main(String[] args) {

        new Thread(() -> {
            // 尝试获取ReentrantLock
            ROOM.lock();
            try {
                log.debug("有烟没?[{}]", hasCigarette);
                while (!hasCigarette) {
                    log.debug("没烟,先歇会!");
                    try {
                        // 进入等烟休息室等待
                        waitCigaretteSet.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                log.debug("可以开始干活了");
            } finally {
                // 解锁
                ROOM.unlock();
            }
        }, "小南").start();

        new Thread(() -> {
            ROOM.lock();
            try {
                log.debug("外卖送到没?[{}]", hasTakeout);
                while (!hasTakeout) {
                    log.debug("没外卖,先歇会!");
                    try {
                        waitTakeoutSet.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                log.debug("可以开始干活了");
            } finally {
                ROOM.unlock();
            }
        }, "小女").start();
        // 送外卖线程
        sleep(1);
        new Thread(() -> {
            ROOM.lock();
            try {
                hasTakeout = true;
                // 唤醒线程
                waitTakeoutSet.signal();
            } finally {
                ROOM.unlock();
            }
        }, "送外卖的").start();

        // 送烟线程
        sleep(1);
        new Thread(() -> {
            ROOM.lock();
            try {
                hasCigarette = true;
                // 唤醒线程
                waitCigaretteSet.signal();
            } finally {
                ROOM.unlock();
            }
        }, "送烟的").start();
    }
}

运行结果:
在这里插入图片描述

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

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

相关文章

浅析 SplitChunksPlugin 及代码分割的意义

本文作者为 360 奇舞团前端开发工程师起因有同事分享webpack的代码分割,其中提到了SplitChunksPlugin,对于文档上的描述大家有着不一样的理解,所以打算探究一下。Q:什么是 SplitChunksPlugin?SplitChunksPlugin 是用来…

Python所有方向的入门和进阶路线,20年老师傅告诉你方法

干了20多年程序员,对于Python研究一直没停过,这几天把我自己对Python的认知和经验,再结合很多招聘网站上的技术要求,整理出了Python所有方向的学习路线图,基本上各个方向应该学什么,都在上面了,…

macOS 13.3 Beta 3 (22E5236f)发布

系统介绍3 月 8 日消息,苹果今日向 Mac 电脑用户推送了 macOS 13.3 开发者预览版 Beta 3 更新(内部版本号:22E5236f),本次更新距离上次发布隔了 7 天。macOS Ventura 带来了台前调度、连续互通相机、FaceTime 通话接力…

文件预览kkFileView安装及使用

1 前言网页端一般会遇到各种文件,比如:txt、doc、docx、pdf、xml、xls、xlsx、ppt、pptx、zip、png、jpg等等。有时候我们不想要把文件下载下来,而是想在线打开文件预览 ,这个时候如果每一种格式都需要我们去写代码造轮子去实现预…

k8s pod调度总结

在Kubernetes平台上,我们很少会直接创建一个Pod,在大多数情况下会通过控制器完成对一组Pod副本的创建、调度 及全生命周期的自动控制任务,如:RC、Deployment、DaemonSet、Job 等。本文主要举例常见的Pod调度。1全自动调度功能&…

第二章:基础语法

第二章:基础语法 2.1:关键字和保留字 关键字 定义:被Java语言赋予了特殊含义,用做专门用途的字符串(单词) 特点:关键字中所有字母都为小写 分类: 用于定义数据类型的关键字 class、interface、enum、byt…

算法设计与分析——递归与分治策略——全排列Perm函数

删除线格式 [toc] 问题描述 现给出m个不同的数字,在n个位置上,对齐进行全排列。使用编程实现数学中全排列输出最终计算结果并将所有的排列打印出来。 思路分析 常规的递归方式进行解决即可,递归的终点是根据题目要求进行实现。共有两个参…

第一次运行vue遇到的问题

1.vue无法识别https://blog.csdn.net/weixin_61634408/article/details/1265897982.yarn serve问题https://blog.csdn.net/fangxuan1509/article/details/104711690/3.关闭控制台报错检查(每次vue-rounter必须用)vue.config,js,的module.exports 中添加l…

【Linux】sudo指令

在本期博客正式开始之前,我们先来解决一个历史遗留问题:sodu指令怎么用不了?sudo指令📌sudo是linux下常用的允许普通用户使用超级用户权限的工具,允许系统管理员让普通用户执行一些或者全部的root命令📋但是…

【预告】ORACLE Unifier v22.12 虚拟机发布

引言 离ORACLE Primavera Unifier 最新系统 v22.12已过去了3个多月,应盆友需要,也为方便大家体验,我近日将构建最新的Unifier的虚拟环境,届时将分享给大家,最终可通过VMWare vsphere (esxi) / workstation 或Oracle …

【Spring6】| Bean的四种获取方式(实例化)

目录 一:Bean的实例化方式 1. 通过构造方法实例化 2. 通过简单工厂模式实例化 3. 通过factory-bean实例化 4. 通过FactoryBean接口实例化 5. BeanFactory和FactoryBean的区别(面试题) 6. 使用FactoryBean注入自定义Date 一&#xff1a…

Radiant:AR/VR显示系统测试比2D屏难在哪?

我们知道,光学一直是AR/VR的核心技术,为了实现理想的光学显示效果,AR/VR厂商和科研人员不断在解决各种各样的问题。除了光学方案外,光学器件的质量对于AR/VR显示效果也很重要。在DSCC举办的一场AR/VR显示论坛上,光学检…

HashMap底层的实现原理(JDK8)

目录一、知识点回顾二、HashMap 的 put() 和 get() 的实现2.1 map.put(k, v) 实现原理2.2 map.get(k) 实现原理三、HashMap 的常见面试题3.1 为何随机增删、查询效率都很高?3.2 为什么放在 HashMap 集合 key 部分的元素需要重写 equals 方法?3.3 HashMap 的 key 为…

HTML DOM 元素

创建新的 HTML 元素在文档对象模型 (DOM) 中,每个节点都是一个对象。DOM 节点有三个重要的属性,分别是:nodeName : 节点的名称nodeValue :节点的值nodeType :节点的类型创建新的 HTML 元素如需向 HTML DOM 添加新元素&…

一款OutLook信息收集工具

OutLook 这是一款burp插件,用于Outlook用户信息收集,在已登录Outlook账号后,可以使用该 插件自动爬取所有联系人的信息 安装 在burp扩展面板加载jar即可 功能介绍 All Users 加载插件后,进入Outlook联系人面板,…

unity开发知识点小结03

物理关节 铰链关节 按照固定的轴进行旋转 弹簧关节 两物体之间加装弹簧 固定关节 两个物体相关联 射线检测 通过射线检测,我们可以实现用鼠标来移动物体,当我们用鼠标点击场景中的某一位置,摄像机就发出一条射线,并且通过…

zookeeper从安装到入门

文章目录什么是zookeeperzookeeper的安装启动zookeeper并检查是否安装完成zookeeper的一些操作指令zookeeper的JavaAPI建立连接创建节点查询节点修改节点删除节点事件监听分布式锁集群leader选举规则集群角色什么是zookeeper ZooKeeper是一个分布式的,开放源码的分…

Fastjson 1.2.24 命令执行漏洞复现-JNDI简单实现反弹shell

文章目录前言一、环境搭建二、漏洞复现准备三、漏洞复现四、不成功的原因(排查):总结前言 网上文章千篇一律,导致很多人都只会一种方法,只要有一种办法就所有人跟着这个办法去做了,新建java文件&#xff0…

Azure Function App Deploy Issue

问题&#xff1a;之前一直用vs code 的 Azure Function extension 工具部署&#xff0c;没有什么问题。直到3月份发现 vs code显示部署成功&#xff0c;但是通过 https://<function_name>.scm.azurewebsites.net/DebugConsole 查看上传后的 dll 文件&#xff0c;dll文件根…

每日学术速递3.8

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.CV 1.Unleashing Text-to-Image Diffusion Models for Visual Perception 标题&#xff1a;释放用于视觉感知的文本到图像扩散模型 作者&#xff1a;Wenliang Zhao, Yongming Rao, Zuya…