JUC并发编程——多把锁

news2025/8/6 6:17:02

一、多八锁

多把不相干的锁
一间大屋子有两个功能:睡觉、学习,互不相干。

现在小南要学习,小女要睡觉,但如果只用一间屋子(一个对象锁)的话,那么并发度很低

解决方法就是准备多个房间(对个对象锁)

例如

import cn.itcast.n2.util.Sleeper;
import lombok.extern.slf4j.Slf4j;

public class TestMultiLock {
    public static void main(String[] args) {
        BigRoom bigRoom = new BigRoom();
        new Thread(() -> {
            bigRoom.study();
        },"小南").start();
        new Thread(() -> {
            bigRoom.sleep();
        },"小女").start();
    }
}

@Slf4j(topic = "c.BigRoom")
class BigRoom {

    public void sleep() {
        synchronized (this) {
            log.debug("sleeping 2 小时");
            Sleeper.sleep(2);
        }
    }

    public void study() {
        synchronized (this) {
            log.debug("study 1 小时");
            Sleeper.sleep(1);
        }
    }
}

运行结果:(并发度较低)
在这里插入图片描述

改进:(加入多把锁)

import cn.itcast.n2.util.Sleeper;
import lombok.extern.slf4j.Slf4j;

public class TestMultiLock {
    public static void main(String[] args) {
        BigRoom bigRoom = new BigRoom();
        new Thread(() -> {
            bigRoom.study();
        },"小南").start();
        new Thread(() -> {
            bigRoom.sleep();
        },"小女").start();
    }
}

@Slf4j(topic = "c.BigRoom")
class BigRoom {

    private final Object studyRoom = new Object();

    private final Object bedRoom = new Object();

    public void sleep() {
        synchronized (bedRoom) {
            log.debug("sleeping 2 小时");
            Sleeper.sleep(2);
        }
    }

    public void study() {
        synchronized (studyRoom) {
            log.debug("study 1 小时");
            Sleeper.sleep(1);
        }
    }
}

改进后运行结果:用多八把锁/细粒度的锁提高程序的并发性需保证业务间无关联
在这里插入图片描述

将锁的粒度细分

class BigRoom {
    //额外创建对象来作为锁
	private final Object studyRoom = new Object();
	private final Object bedRoom = new Object();
}

● 好处:可以增强并发度
● 坏处:如果一个线程需要同时获取多把锁,就容易发生死锁

二、活跃性

2.1 死锁

有这样的情况:一个线程需要同时获取多把锁,这时就容易发生死锁

t1 线程 获得 A对象 锁,接下来想获取 B对象 的锁
t2 线程 获得 B对象 锁,接下来想获取 A对象 的锁

例:各自均有一把锁,都想获取对方锁时会产生死锁,代码最终无法得到执行

import lombok.extern.slf4j.Slf4j;

import static cn.itcast.n2.util.Sleeper.sleep;

@Slf4j(topic = "c.TestDeadLock")
public class TestDeadLock {
    public static void main(String[] args) {
        test1();
    }

    private static void test1() {
        Object A = new Object();
        Object B = new Object();
        // t1线程获取A对象上的锁
        Thread t1 = new Thread(() -> {
            synchronized (A) {
                log.debug("lock A");
                sleep(1);
                // t1线程经1s后尝试获取锁B
                synchronized (B) {
                    log.debug("lock B");
                    log.debug("操作...");
                }
            }
        }, "t1");

        // t2线程获取B对象上的锁
        Thread t2 = new Thread(() -> {
            synchronized (B) {
                log.debug("lock B");
                sleep(0.5);
                // t2线程经0.5s后尝试获取锁A
                synchronized (A) {
                    log.debug("lock A");
                    log.debug("操作...");
                }
            }
        }, "t2");
        t1.start();
        t2.start();
    }
}

2.2 定位死锁

死锁在多线程是较常见的。检测死锁可以使用 jconsole工具,或者使用 jps 定位进程 id,再用 jstack 定位死锁:

:使用 jps 定位进程 id,再用 jstack 定位
在这里插入图片描述

打印死锁信息
在这里插入图片描述

:使用jconsole工具

运行jconsole
在这里插入图片描述

在这里插入图片描述

监测出出现错误的代码行数
在这里插入图片描述
【活跃性】问题,除了死锁以外,还有活锁和饥饿者两种情

2.3 哲学家就餐问题

在这里插入图片描述
有五位哲学家,围坐在圆桌旁。

● 他们只做两件事,思考和吃饭,思考一会吃口饭,吃完饭后接着思考。
● 吃饭时要用两根筷子吃,桌上共有 5 根筷子,每位哲学家左右手边各有一根筷子。
● 如果筷子被身边的人拿着,自己就得等待

测试结果:
正常就餐几轮后不能再继续就餐出现无限等待(每个哲学家各持有一根筷子,等待对方放下筷子)

2.4 活锁

活锁出现在两个线程互相改变对方的结束条件,最后谁也无法结束。

例如

import lombok.extern.slf4j.Slf4j;
import static cn.itcast.n2.util.Sleeper.sleep;

@Slf4j(topic = "c.TestLiveLock")
public class TestLiveLock {
    static volatile int count = 10;
    static final Object lock = new Object();

    public static void main(String[] args) {
        new Thread(() -> {
            // 期望减到 0 退出循环
            while (count > 0) {
                sleep(0.2);
                count--;
                log.debug("count: {}", count);
            }
        }, "t1").start();
        new Thread(() -> {
            // 期望超过 20 退出循环
            while (count < 20) {
                sleep(0.2);
                count++;
                log.debug("count: {}", count);
            }
        }, "t2").start();
    }
}

运行结果:(不断在10左右进行加加减减操作,线程失踪无法停止)
在这里插入图片描述

死锁与活锁的区别:
死锁是两个线程持有对方需要的锁,导致线程都无法继续向下运行,两个线程均陷入阻塞;而活锁两个线程均未阻塞,都在使用CPU不断运行,但由于改变对方的结束条件导致两个线程都无法结束

解决活锁的办法
● 使两个线程的执行时间有一定的交错(不集中在一起执行/设置睡眠的时间为一个随机数===>将其指令交错开,第一个线程很快运行完,第二个线程将没有机会改变对方的结束条件)

在开发中遇到活锁的情况,可增加随机睡眠时间来避免活锁的产生

2.5 饥饿

很多教程中将饥饿定义为,一个线程由于优先级太低,始终得不到 CPU 调度执行,也不能够结束,饥饿的情况不易演示,讲读写锁时会涉及饥饿问题

观察一个线程饥饿的例子,可以先观察使用顺序加锁的方式解决之前的死锁问题
在这里插入图片描述

相同顺序加锁的解决方案
在这里插入图片描述

线程1按AB的顺序加锁,先获得锁A,线程2也是按AB的顺序进行加锁,线程2此时想获取对象A的锁时获取失败(进入对象A的EntryList中阻塞),这时线程1再尝试获取B对象的锁。这样线程1均可以将AB两个锁获取到。等其释放完后线程2也按相同的顺序获取AB对象。

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

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

相关文章

Validator校验之ValidatorUtils

注意&#xff1a;hibernate-validator 与 持久层框架 hibernate 没有什么关系&#xff0c;hibernate-validator 是 hibernate 组织下的一个开源项目 。 hibernate-validator 是 JSR 380&#xff08;Bean Validation 2.0&#xff09;、JSR 303&#xff08;Bean Validation 1.0&…

python实现半色调技术图像转换

半色调技术 半色调技术是一种将灰度图像转换为黑白图像的技术。它是通过将灰度图像的像素值映射到黑白像素值上来实现的。 比如说&#xff0c;在一块只能显示纯黑或纯白的屏幕上&#xff0c;如何将一张灰度图显示出灰度的效果&#xff0c;这时就可以用半色调技术实现。 如下…

SpringBoot整合MybatisPlus

文章目录前言一、MybatisPlus是什么&#xff1f;二、使用步骤1.导入依赖2.编写配置文件3.编写Controller和实体类4.编写持久层接口mapper5.启动类加包扫描注解6.测试总结前言 本篇记录一下SpringBoot整合MybatisPlus 一、MybatisPlus是什么&#xff1f; MyBatis-Plus&#xff…

webview的工作、内存泄漏、漏洞以及缓存机制原理原理+方案解决

分析一段appium的日志来分析webview的工作原理&#xff0c;文章尾部附有自动化脚本及完整日志&#xff1a; 解析&#xff1a; 获取上下文列表 服务端发送命令adb shell cat /proc/net/unix获取域套接字列表。那什么是域套接字呢&#xff1f; 域套接字&#xff1a;是unix系统里…

旋转矩阵的作用:世界坐标变换;求解局部坐标系下的局部坐标

旋转矩阵的作用&#xff1a;世界坐标变换&#xff1b;求解局部坐标系下的局部坐标1、旋转矩阵代表了一个局部坐标系2、世界坐标变换3、求解局部坐标系下的局部坐标1、旋转矩阵代表了一个局部坐标系 以下数据以平面直角坐标系为例&#xff0c;三维空间同理 上图中&#xff0c;…

第七章:Java常用类

第七章&#xff1a;Java常用类 7.1&#xff1a;字符串相关的类 String的特性 String表示是字符串&#xff0c;使用一对""引起来表示。 String声明为final的&#xff0c;不可被继承。 String实现了Serializable、Comparable接口&#xff0c;表示字符是支持序列化和…

【WPF 值转换器】ValueConverter 进阶用法

【WPF 值转换器】ValueConverter 进阶用法介绍基类实现子类实现效果介绍 值转换器在WPF开发中是非常常见的&#xff0c;当然不仅仅是在WPF开发中。值转换器可以帮助我们很轻松地实现&#xff0c;界面数据展示的问题&#xff0c;如&#xff1a;模块隐藏显示、编码数据展示为可读…

拼多多跨境电商业务将正式登陆澳洲,持续助力中国制造业开拓全球市场

配图来自Canva可画 拼多多出海又有新进展。据最新消息&#xff0c;拼多多跨境电商业务将于3月13日正式进入澳大利亚、新西兰&#xff0c;持续推动中国制造业直连澳洲以及全球市场。 去年9月&#xff0c;拼多多正式在北美地区上线跨境电商平台TEMU。自上线至今&#xff0c;TEM…

硬件设备二 调试分类、软/硬件断点、OpenOCD、JLink、STLink 使用

近期工作开始主用 OpenOCD 来进行相关开发工作的调试&#xff0c;因此本文重点来学习一下&#xff01;本文以 OpenOCD 为重点&#xff0c;辅以 JLink、STLink 作为对比来进行学习。 调试 本地调试 本地调试通常指调试使用的环境与调试的目标运行环境一致的调试方法&#xff0…

linux软件管理

软件管理 1、rpm管理工具 RPM是(Redhat Package Manager) 的缩写&#xff0c;由Red Hat公司提出的一种软件包管理标准 &#xff08;1&#xff09;rpm软件包的典型命名格式&#xff1a; 例&#xff1a;zsh-5.0.2-14.el8.x86_64.rpm zsh-50214el8x8664软件名主版本号次版本号…

day26_HTML

今日内容 上课同步视频:CuteN饕餮的个人空间_哔哩哔哩_bilibili 同步笔记沐沐霸的博客_CSDN博客-Java2301 零、 复习昨日 一、二阶段介绍 二、HTML 零、 复习昨日 见代码 一、二阶段介绍 第一阶段: 基础入门 java基本语法编程基础(方法,数组)面向对象编程常用类高级(IO,线程,新…

新增 IEC61850、Profinet 驱动

2 月&#xff0c;Neuron 团队主要侧重于开发新的驱动&#xff0c;新增了南向 IEC61850 驱动、南向 Allen-Bradley DF1 驱动、Profinet 驱动支持以及静态点位等功能&#xff0c;这些新驱动和新功能将在 2.4 版本中正式发布。 IEC61850 驱动 IEC61850 标准是电力系统自动化领域…

网络基础(2)

目录1. 端口号2. 套接字socket3. 网络通信3.1 sockaddr与sockaddr_in3.2 接口服务端3.2.1 创建套接字&#xff0c;打开网络文件3.2.2 给该服务器绑定端口和ip&#xff08;特殊处理&#xff09;3.2.3 初始化相关服务器3.2.4 提供服务客户端3.2.5 绑定3.2.6 使用服务4. makefile实…

关于 JavaScript 中的 Promises

在 JavaScript 中&#xff0c;Promise 是一个对象&#xff0c;它表示一个可能还不可用&#xff0c;但会在未来解决的值。Promises 用于处理异步操作&#xff0c;例如发出网络请求或访问数据库&#xff0c;其中结果不是立即可用的。如果你准备好了&#xff0c;我想开始我们的冒险…

基于SpringBoot+SpringCloud+Vue前后端分离项目实战 --开篇

本文目录前言做项目的三大好处强强联手(天狗组合)专栏作者简介专栏的优势后端规划1. SpringBoot 和 SpringCloud 的选择2. Mybatis 和 MybatisPlus 和 JPA 的选择3. MySQL 和 Mongodb 的选择4. Redis 和 RocketMQ5. 后端规划小总结后端大纲提前掌握的知识点一期SpringBoot二期S…

EventLoop机制

JavaScript 是单线程的语言 JavaScript 是一门单线程执行的编程语言。也就是说&#xff0c;同一时间只能做一件事情。 单线程执行任务队列的问题&#xff1a; 如果前一个任务非常耗时&#xff0c;则后续的任务就不得不一直等待&#xff0c;从而导致程序假死的问题。 同步任…

机器学习和深度学习的综述

机器学习和深度学习的综述 1.1.1人工智能、机器学习、深度学习的关系 人工智能&#xff08;Artificial Intelligence&#xff0c;AI&#xff09;是最宽泛的概念&#xff0c;是研发用于模拟、延伸和扩展人的智能的理论、方法、技术及应用系统的一门新的技术科学。由于这个定义…

代码随想录算法训练营第二十一天打卡 | 530.二叉搜索树的最小绝对差、501.二叉搜索树中的众数、236. 二叉树的最近公共祖先

打卡第21天&#xff0c;继续二叉树&#xff0c;前几天终于补完了&#xff0c;感觉难度上来了。 今日任务 530.二叉搜索树的最小绝对差501.二叉搜索树中的众数 二叉树的最近公共祖先 530.二叉搜索树的最小绝对差 给你一个二叉搜索树的根节点 root &#xff0c;返回 树中任意两不…

计算机网络——路由、交换、子网篇

子网掩码 解释1 作者&#xff1a;纪言 链接&#xff1a;https://www.zhihu.com/question/56895036/answer/447915532 来源&#xff1a;知乎 著作权归作者所有。商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处。 仅限ip4首先子网掩码这名字取得很好&#xff0c…

Java创建线程的方式只有一种:Thread+Runnable

Java创建线程的方式其实只有一种&#x1f468;‍&#x1f393;一、继承Thread&#x1f468;‍&#x1f393;二、实现Runnable接口&#x1f468;‍&#x1f393;三、实现Callable接口&#x1f468;‍&#x1f393;四、通过线程池创建&#x1f468;‍&#x1f393;五、总结一般我…