JUC:ReentrantLock(可打断、锁超时、多条件变量)

文章目录

  • ReentrantLock
    • 特点
    • 基本语法
    • 可重入
    • 可打断(避免死等、被动)
    • 锁超时(避免死等、主动)
    • 公平锁
    • 多个条件变量

ReentrantLock

翻译:可重入锁

特点

  • 可中断
  • 可设置超时时间(不会一直等待锁)
  • 可设置为公平锁(防止线程饥饿)
  • 支持多个条件变量
  • 与 synchronized 一样可重入

基本语法

// 获取锁
reentrantLock.lock();
try {
    // 临界区
} finally {
    // 释放锁
    reentrantLock.unlock();
}

可重入

同一个线程如果已经拥有了锁,可以再次获得这把锁。

@Slf4j(topic = "c.Test3")
public class st3 {
    private static ReentrantLock lock = new ReentrantLock();

    public static void main(String[] args) throws InterruptedException {

        lock.lock(); // 加锁
        try {
            log.debug("enter m1");
            m1();
        }finally {
            lock.unlock(); // 解锁
        }
    }

    public static void m1 () {
        lock.lock();
        try {
            log.debug("enter m2");
            m2();
        } finally {
            lock.unlock();
        }
    }

    public static void m2 () {
        lock.lock();
        try {
            log.debug("m2获取了锁");
        } finally {
            lock.unlock();
        }
    }
}

在这里插入图片描述

可打断(避免死等、被动)

lockInterruptibly 可打断锁,在尝试获取锁进入阻塞队列中,被打断时,可以停止等待锁。

 public static void main(String[] args) throws InterruptedException {

        ReentrantLock lock = new ReentrantLock();

        Thread t1 = new Thread(() -> {

            try {
                log.debug("t1线程尝试获取锁");
                lock.lockInterruptibly(); // 尝试获取锁。若有竞争,进入阻塞队列,可被打断
            } catch (InterruptedException e) {
                e.printStackTrace();
                log.debug("等待锁的时候被打断了,停止等待,返回");
                return;
            }

            try {
                log.debug("获得了锁");
            } finally {
                lock.unlock();
            }
        }, "t1");
        lock.lock();
        log.debug("主线程获得了锁");
        t1.start();

        sleep(1);
        log.debug("打断 t1");
        t1.interrupt();
        lock.unlock();
    }

锁超时(避免死等、主动)

无参数:获取锁,若没获取到,立即失败,返回false。

@Slf4j(topic = "c.Test3")
public class st3 {
    public static void main(String[] args) throws InterruptedException {

        ReentrantLock lock = new ReentrantLock();

        Thread t1 = new Thread(() -> {
            log.debug("启动...");
            if (!lock.tryLock()) {
                log.debug("t1获取锁立刻失败,返回");
                return;
            }
            try {
                log.debug("t1获得到了锁");
            } finally {
                lock.unlock();
            }
        }, "t1");
        lock.lock();
        log.debug("主线程获得了锁");
        t1.start();
        sleep(2);
        lock.unlock();
    }
}

在这里插入图片描述

可以主动设置等待时间。

@Slf4j(topic = "c.Test3")
public class st3 {
    public static void main(String[] args) throws InterruptedException {

        ReentrantLock lock = new ReentrantLock();

        Thread t1 = new Thread(() -> {
            log.debug("启动...");

            try {
                if (!lock.tryLock(1, TimeUnit.SECONDS)) {
                    log.debug("尝试获取锁1s,失败,返回");
                    return;
                }
            } catch (InterruptedException e) { // 被打断,应该也return
                e.printStackTrace();
            }
            try {
                log.debug("t1获得了锁");
            } finally {
                lock.unlock();
            }
        }, "t1");

        lock.lock();
        log.debug("主线程获得了锁");
        t1.start();
        sleep(1);
        lock.unlock();
    }
}

主线程1ms后释放了锁,t1线程还在1s的等待中,可以获得锁,若时等待超过1s那就不再尝试获取锁了。

在这里插入图片描述

若主线程沉睡1s的情况:

sleep(1000);

在这里插入图片描述

公平锁

ReentrantLock默认也是不公平锁,但是可以修改。

公平: 先来先执行
不公平: 随机有机会

这里就给出如何修改

ReentrantLock lock = new ReentrantLock(true);  // 公平
ReentrantLock lock = new ReentrantLock(false); // 不公平  

一般是不设置的,会降低并发度,所以就不给出例子了。

多个条件变量

  • ReentrantLock 的条件变量比 synchronized 强大之处在于,它是支持多个条件变量的。
  • synchronized只能全部都唤醒,而ReentrantLock可以根据条件唤醒对应线程。
Condition waitCigaretteQueue = lock.newCondition(); // 条件
waitCigaretteQueue.await(); // 因为此原因进入等待  

比如,在操作系统里,有一个很经典的问题,爸爸向盘子里放苹果,妈妈向盘子里放香蕉,儿子只吃苹果,女儿只吃香蕉,盘子可以放多个水果,但每种最多一个。

如果我么只用sync锁,那么就只能有一个条件,如果只有爸爸向盘子里放了苹果,那么他会唤醒儿子和女儿,但其实只唤醒儿子即可。
所以利用ReentrantLock多条件,因为沉睡的条件不同,我么就可以对应的唤醒我们想要的一些线程。
这里例子是用的烟和早餐,一样的。

@Slf4j(topic = "c.Test4")
public class st4 {
    static ReentrantLock lock = new ReentrantLock();

    static Condition waitCigaretteQueue = lock.newCondition(); //因为等烟等烟,进入等待的阻塞线程队列
    static Condition waitTokeoutQueue = lock.newCondition(); //因为等早餐进入阻塞队列

    static boolean hasCigarette = false;
    static boolean hasTokeout = false;

    public static void main(String[] args) throws InterruptedException {

        new Thread(() -> {
            lock.lock();
            try {
                while (!hasCigarette) {
                    try {
                        waitCigaretteQueue.await(); // 因为等烟,进入等待
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                log.debug("等到了烟");
            } finally {
                lock.unlock(); // 解锁
            }
        }).start();

        new Thread(() -> {
            try {
                lock.lock();
                while (!hasTokeout) {
                    try {
                        waitTokeoutQueue.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                log.debug("等到了它的早餐");
            } finally {
                lock.unlock();
            }
        }).start();

        sleep(1);
        sendBreakfast();
        sleep(1);
        sendCigarette();
    }

    private static void sendCigarette() {
        lock.lock();
        try {
            log.debug("送烟来的了");
            hasCigarette = true;
            waitCigaretteQueue.signal();
        } finally {
            lock.unlock();
        }
    }

    private static void sendBreakfast() {
        lock.lock();
        try {
            log.debug("送早餐的来了");
            hasTokeout = true;
            waitTokeoutQueue.signal();
        } finally {
            lock.unlock();
        }
    }
}

在这里插入图片描述

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

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

相关文章

算法学习——LeetCode力扣动态规划篇10(583. 两个字符串的删除操作、72. 编辑距离、647. 回文子串、516. 最长回文子序列)

算法学习——LeetCode力扣动态规划篇10 583. 两个字符串的删除操作 583. 两个字符串的删除操作 - 力扣(LeetCode) 描述 给定两个单词 word1 和 word2 ,返回使得 word1 和 word2 相同所需的最小步数。 每步 可以删除任意一个字符串中的一个…

Last-Modified:HTTP缓存控制机制解析

🤍 前端开发工程师、技术日更博主、已过CET6 🍨 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 🕠 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 🍚 蓝桥云课签约作者、上架课程《Vue.js 和 E…

nodejs下载安装以及npm、yarn安装及配置教程

1、nodejs下载安装 ​ 1.1、使用nodejs版本管理工具下载安装,可一键安装、切换不同nodejs版本, nvm-setup.zip:安装版,推荐使用 本次演示的是安装版。 1、双击安装文件 nvm-setup.exe 选择nvm安装路径 例如:E:\Soft…

蓝桥杯算法题-图形排版

题目描述 小明需要在一篇文档中加入 N 张图片,其中第 i 张图片的宽度是 Wi,高度是 Hi。   假设纸张的宽度是 M,小明使用的文档编辑工具会用以下方式对图片进行自动排版: 1. 该工具会按照图片顺序,在宽度 M 以内&…

Mysql数据库:MHA高可用架构

目录 前言 一、MHA概述 1、什么是MHA 2、MHA的特点 3、MHA的组成 4、MHA的工作原理 5、故障切换备选主库的算法 二、部署MHA高可用架构 1、环境部署 2、部署主从同步 2.1 修改主配置文件并创建软链接 2.1.1 master 修改主配置文件并创建软连接 2.1.2 slave1 修改主…

【JavaSE】类和对象详解(下)

前言 面向对象程序的三大特性:封装、继承、多态~ 书接上回 类和对象(上)~ 欢迎关注个人主页:逸狼 创造不易,可以点点赞吗~ 如有错误,欢迎指出~ 目录 前言 封装 private public 快速生成可访问封装的方法 包…

ubuntu16.04 不支持 gcc-11,g++11

总结 ubuntu16.04 不支持 gcc-11,需要升级 18.04 或更高的版本。 背景 最近需要在我的 ubuntu16.04 电脑上安装 gcc-11,g-11,使用更高的版本来编译代码。根据网上查到的方式是添加以下的源并进行安装 sudo add-apt-repository ppa:ubuntu…

数据库之MyBatisPlus详解

MyBatisPlus MyBatis-Plus (opens new window)(简称 MP)是一个 MyBatis (opens new window) 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。 官网地址:https://baomidou.com/ 一、入门案…

微信公众号迁移公证书在哪?

公众号迁移有什么作用?只能变更主体吗?很多小伙伴想做公众号迁移,但是不知道公众号迁移有什么作用,今天跟大家具体讲解一下。首先公众号迁移最主要的就是修改公众号的主体了,比如我们公众号原来是A公司的,现…

Ruoyi-Cloud-Plus_使用Docker部署分布式微服务系统_环境准备_001---SpringCloud工作笔记200

1.首先安装docker: 如果以前安装过首先执行: yum remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-selinux docker-engine-selinux docker-engine 去卸载docker 2.安装dokcer需要的工具包…

linux下minio部署和nginx配置

1 下载minio wget https://dl.min.io/server/minio/release/linux-amd64/minio chmod x minio #启动minio,文件数据存放在/data目录 ./minio server /data2 部署minio 下载minio后赋予可执行权限就可以运行了,这里我整理了遇到的坑和解决问题的最终配置…

大模型面试准备(九):简单透彻理解MoE

节前,我们组织了一场算法岗技术&面试讨论会,邀请了一些互联网大厂朋友、参加社招和校招面试的同学,针对大模型技术趋势、大模型落地项目经验分享、新手如何入门算法岗、该如何备战、面试常考点分享等热门话题进行了深入的讨论。 合集在这…

【洛谷】P9241 [蓝桥杯 2023 省 B] 飞机降落

挺有意思的一道题,分享给大家 题目链接 P9241 [蓝桥杯 2023 省 B] 飞机降落 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 题目描述 输入格式 输出格式 输入输出样例 说明/提示 思路 一开始尝试贪心能不能做,但是不好确定飞机的顺序 因为这题的数…

第5篇:挖矿病毒(一)

0x00 前言 ​ 随着虚拟货币的疯狂炒作,挖矿病毒已经成为不法分子利用最为频繁的攻击方式之一。病毒传播者可以利用个人电脑或服务器进行挖矿,具体现象为电脑CPU占用率高,C盘可使用空间骤降,电脑温度升高,风扇噪声增大…

金融衍生品市场

金融衍生品市场 衍生金融品的作用衍生金融工具远期合约期货合约期权 衍生金融品的作用 套期保值(Hedging) 组合多头头寸(long position)与空头头寸(short position)例:股票与股指期货 投机 衍生金融工具 远期合约 定义:在将来…

C语言数组详解

一维数组 创建和初始化 数组就是一组相同元素的集合。 他的创建: char arr[10]; int arr1[5]; 数组创建中 [] 里不能是变量,但是在c99标准之后就可以了被称为变长数组,但是不常用,而且变长数组不能初始化。 初始化&#xff…

这两个比较经典的LVS问题怎么解?

今日景芯SoC VIP学员遇到的LVS问题,比较经典,大家先思考下。然后本文末尾分享下2.5GHz A72训练营的LVS解法: 问题1: 问题2: 修改后: 具体修改方案参见知识星球,接下来分享下2.5GHz A72项目的LV…

如何使用固定公网地址远程访问内网Axure RP生成的网站原型web页面

文章目录 前言1.在AxureRP中生成HTML文件2.配置IIS服务3.添加防火墙安全策略4.使用cpolar内网穿透实现公网访问4.1 登录cpolar web ui管理界面4.2 启动website隧道4.3 获取公网URL地址4.4. 公网远程访问内网web站点4.5 配置固定二级子域名公网访问内网web站点4.5.1创建一条固定…

StructStreaming Batch mode和Continuous mode

StructStreaming Batch mode和Continuous mode 让我们把目光集中到 Structured Streaming,也就是流处理引擎本身。Structured Streaming 与 Spark MLlib 并列,是 Spark 重要的子框架之一。值得一提的是,Structured Streaming 天然能够享受 S…

C语言刷题总结

1.网购问题 (1)这道题目需要分多种情况进行考虑:双11还是双12,有无优惠券,是否会出现优惠券的面值大于购买商品的单价(这个时候直接按0元进行处理); (2)在对于优惠券的分…