锁策略和死锁问题

锁策略

  • 乐观锁 vs 悲观锁
  • 重量级锁 vs 轻量级锁
  • 自旋锁 vs 挂起等待锁
  • 读写锁 vs 互斥锁
  • 公平锁 vs 非公平锁
  • 可重入锁 vs 不可重入锁
  • 死锁
    • 死锁产生的必要条件
    • 如何简单的解决死锁问题
  • 小结

这里不是描述的某个特定锁,而是描述的锁的特性,描述的是"一类锁".

乐观锁 vs 悲观锁

乐观锁: 预测在该场景中,不太会出现锁冲突的情况.
悲观锁: 预测在该场景中,非常容易出现锁冲突.
锁冲突: 两个线程尝试获取同一把锁,一个线程能获取成功,另一个线程阻塞等待.

重量级锁 vs 轻量级锁

重量级锁: 加锁的开销比较大(花的时间多,占用系统资源多), 大多是悲观锁.
轻量级锁: 加锁的开销比较小(花的时间少,占用系统资源少), 大多是乐观锁.

自旋锁 vs 挂起等待锁

自旋锁: 是轻量级锁的一种典型实现,在用户态下,通过自旋的方式(while循环), 实现加锁效果的.
自旋锁会消耗一定的cpu资源,但是可以做到最快速度拿到锁.
挂起等待锁: 是重量级锁的一种典型实现, 通过内核态,借助系统提供的锁机制,当出现锁冲突的时候,
会牵引到内核对于线程的调度,使冲突的线程阻塞等待.
挂起等待锁消耗的cpu资源更少,无法保证第一时间拿到锁.

读写锁 vs 互斥锁

读写锁: 很多读数据的线程之间并不互斥,而写操作要求与任何人互斥.
1. 两个线程,一个线程读加锁,另一个线程也是读加锁,不会产生锁竞争.
2. 两个线程,一个线程读加锁,另一个线程是写加锁,会产生锁竞争.
3. 两个线程,一个线程写加锁,另一个线程也是写加锁,会产生锁竞争.
互斥锁: 锁本身是靠互斥性发挥作用的.

公平锁 vs 非公平锁

公平锁: 遵守先来后到的锁.
非公平锁: 那就是不遵守先来后到的锁.
操作系统内部的线程调度是随机的,如果不做任何额外的限制,锁就是非公平锁.
要想实现公平锁,就需要一些额外的数据结构来支持.(需要记录每个线程的阻塞等待的时间).

可重入锁 vs 不可重入锁

不可重入锁: 一个线程,针对同一把锁,连续加锁两次,产生死锁了.
可重入锁: 一个线程,针对同一把锁,连续加锁两次,没产生死锁.
public synchronized void add() {
	synchronized (this) {
		count++;
	}
}
// 先调用方法add(),这里假设加锁成功.
// 接下来进入代码块,再次针对this对象加锁.

我们对this对象加两次锁(如果是静态方法,是针对类对象加锁.普通方法是针对this对象加锁),此时就会产生锁竞争.(这里写的synchronized是可重入锁,并不会产生死锁,只是用它来表示锁)
为什么呢?
在代码块中,this上的锁必须要等到add方法执行完毕释放后,才能释放.可是这里的逻辑是又加了一个锁,add方法没有释放锁,第二次加锁进入阻塞等待,这里就一直等下去了,也就是产生死锁了.

如果是不可重入锁,这把锁不会保存,是哪个线程对它进行加锁,只要它处于加锁状态,
又收到了"加锁"请求,就会拒绝加锁,此时就会产生死锁.
如果是可重入锁,则是会让这把锁保存,是哪个线程对它进行加锁,就会先对比一下,
看看加锁的线程是不是持有这把锁的线程,就可以灵活判定了.
synchronized 是可重入锁.
public synchronized void add() {
	synchronized (this) {
		count++;
		synchronized (this) {
			count++;
		}
	}
}
// 只有第一个synchronized真正加锁了,后面的都只是虚晃一枪,并没有真正加锁.
// 释放锁也是在最外层的synchrinized中释放的.
// 那么我们怎么知道哪个是最后一个锁呢?
// 让锁这里有一个计数器就可以了

死锁

死锁: 多个线程同时被阻塞,它们中的一个或者全部都在等待资源释放.由于线程无限被阻塞,因此程序不可能正常终止.

  1. 一个线程, 一把锁, 是不可重入锁, 该线程连续加锁两次, 就会出现死锁.
  2. 两个线程, 两把锁, 这两个线程先分别获取到一把锁, 然后再同时尝试获取对方的锁.
public class Demo27 {
    private static Object locker1 = new Object();
    private static Object locker2 = new Object();
    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            synchronized (locker1) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                synchronized (locker2) {
                    System.out.println("thread1加入了两把锁");
                }
            }

        });

        Thread thread2 = new Thread(() -> {
            synchronized (locker2) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                synchronized (locker1) {
                    System.out.println("thread2加入了两把锁");
                }
            }

        });
        thread1.start();
        thread2.start();
    }
}

此时代码就会进入阻塞等待,thread1要想结束,就必须获取到locker2, thread2要想结束,就必须获取到locker1,这两个线程互相冲突,构成死锁.
在这里插入图片描述
3. N个线程M把锁
这里有一个哲学家就餐问题.
假设有5个哲学家, 5根筷子,哲学家主要做两件事,

  1. 思考人生,放下筷子.
  2. 吃面,会分别拿起左手和右手的筷子,再去夹面条吃.

基于上述模型,绝大多数情况下,这些哲学家都可以很好的工作的.但是如果出现极端情况,就会出现死锁.比如说,同一时刻,5个哲学家都想吃面,同时拿到左手的筷子,此时拿不到右手的筷子,就会进入阻塞等待,会进入死锁.

死锁产生的必要条件

只要破坏其中任意一个条件,就可以避免出现死锁.

  1. 互斥使用: 一个线程获取到一把锁后,别的线程不能获取到这个锁.(锁的基本特性)
  2. 不可抢占: 锁只能被持有者主动释放, 而不能被其它线程直接抢走.
  3. 请求和保持: 一个线程去尝试获取多把锁, 在获取多把锁的过程中, 会保持对第一把锁的获取状态.
  4. 循环等待: 相当于thread1要想结束,就必须获取到locker2, thread2要想结束,就必须获取到locker1,这种情况.

如何简单的解决死锁问题

针对锁进行编号, 并且规定加锁的顺序.约定,每个线程如果要获取多把锁, 就必须先获取编号小的锁, 后获取编号大的锁.
只要所有的线程加锁顺序,严格遵守上述情形, 就一定不会出现循环等待.
比如在哲学家就餐问题中, 给每个筷子(锁)进行编号, 按照编号从小到大获取,就不会出现循环等待.

小结

本篇博客讲述了关于锁的特性, 有不足的地方还请多多补充, 希望有收获的小伙伴多多支持.

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

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

相关文章

人到中年三两事儿

人到中年,常常伴随着一系列的焦虑和烦恼。这些焦虑可能源自对工作的不确定性、对未来的担忧、对家庭责任的增加,或是对个人成就的反思。在这个年纪,我们可能会发现自己站在人生的十字路口,面临着重要的选择和决策。 首先&#xff…

数智时代的AI人才粮仓模型解读白皮书(2024版)

来源:极客邦 自 2023 年上半年起,ChatGPT 等大模型技术蓬勃发展,AI 技术不断突破边界,展现 出惊人的潜力和发展速度。从早期的逻辑推理、专家系统,到如今的深度学习、神经网络, AI 技术显著缩小了科学与实…

【面试经典 150 | 二分查找】搜索旋转排序数组

文章目录 写在前面Tag题目来源解题思路方法一:二分查找 写在最后 写在前面 本专栏专注于分析与讲解【面试经典150】算法,两到三天更新一篇文章,欢迎催更…… 专栏内容以分析题目为主,并附带一些对于本题涉及到的数据结构等内容进行…

Redis中的Lua脚本(二)

Lua脚本 创建排序辅助函数 为了防止带有副作用的函数令脚本产生不一致的数据,Redis对math库的math.random函数和math.randomseed函数进行了替换。对于Lua脚本来说,另一个可能产生不一致数据的地方是哪些带有不确定性质的命令,比如对于一个集…

STM32串口通信

一、串口发送 1.初始化引脚 void Serial_Init(uint32_t BaudRate) {RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOA ,ENABLE );RCC_APB2PeriphClockCmd (RCC_APB2Periph_USART1 ,ENABLE );GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP…

python自动化之网易自动点歌

这个代码是是使用的pyautogui库和pyperclip库完成的,这个库是开源的地址如下:https://github.com/asweigart/pyautogui这里详细的用法想学习的可以到这看看 下面是代码: import pyautogui import subprocess import pyperclip import time i…

如何进行 ICP 备案/公安部联网备案

👨🏻‍💻 热爱摄影的程序员 👨🏻‍🎨 喜欢编码的设计师 🧕🏻 擅长设计的剪辑师 🧑🏻‍🏫 一位高冷无情的全栈工程师 欢迎分享 / 收藏 / 赞 / 在看…

安装jmeter和ant

安装jmeter和ant 安装java环境 安装jdk和jre 下载Java SE Development Kit 8 Java SE subscribers will receive JDK 8 updates until at least December 2030. 选择指定包进行安装,如windows 共享账号参考:Oracle官网 账号及密码 目前官网下载低…

Java程序生成可执行的exe文件 详细图文教程

1.Java编辑器,如:idea、eclipse等,下载地址:IntelliJ IDEA: The Capable & Ergonomic Java IDE by JetBrainshttps://www.jetbrains.com/idea/2.exe4j,下载地址:ej-technologies - Java APM, Java Prof…

吴恩达<用于LLM应用程序开发的LangChain> L1-Model_prompt_parser

问题预览/关键词 课程地址如何获取openAI的API Key如何根据日期设置不同模型?如何调用OpenAI的API?如何使用OpenAI的API?langchain如何抽象OpenAI的API接口?langchain如何创建提示词模板并查看模板内容?langchain如何使用提示词模板生成提…

使用Google reCAPTCHA防止机器注册

本文作者:陈进坚 博客地址:https://jian1098.github.io CSDN博客:https://blog.csdn.net/c_jian 简书:https://www.jianshu.com/u/8ba9ac5706b6 联系方式:jian1098qq.com 环境要求 能翻墙的电脑域名 验证原理 在谷歌…

python3--lxml pytoml.core.TomlError expected_equals报错解决

文章目录 一、问题二. 解决方法:三. 参考:四. 总结 一、问题 在ubuntu的armbian上的python3中安装lxml时报错了 安装命令是 pip3 install lxml报错简略信息如下图 File "/usr/share/python-wheels/pytoml-0.1.2-py2.py3-none-any.whl/pytoml/par…

k8s 控制器StatefulSet原理解析

🐇明明跟你说过:个人主页 🏅个人专栏:《Kubernetes航线图:从船长到K8s掌舵者》 🏅 🔖行路有良友,便是天堂🔖 目录 一、前言 1、k8s概述 2、有状态服务和无状态服务…

intelli J中添加maven依赖显示unable to import Maven project?如何解决??

🏆本文收录于「Bug调优」专栏,主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&…

计算机网络练习-计算机网络体系结构与参考模型

计算机网络分层结构 ----------------------------------------------------------------------------------------------------------------------------- 1.在ISO/OSI参考模型中,实现两个相邻结点间流量控制功能的是( )。 A.物理层 B. 数据链路层 C.网络层 D.传…

图像分割:Pytorch实现UNet++进行医学细胞分割

图像分割:Pytorch实现UNet进行医学细胞分割 前言相关介绍项目结构具体步骤准备数据集读取数据集设置并解析相关参数定义网络模型定义损失函数定义优化器训练验证 参考 前言 由于本人水平有限,难免出现错漏,敬请批评改正。更多精彩内容&#x…

50-RGMIISGMIIQGMII电路设计

视频链接 RGMII&SGMII&QGMII电路设计01_哔哩哔哩_bilibili RGMII & SGMII & QSGMII电路设计 1、以太网简介(参考第2课:千兆以太网电路设计) 1.1、以太网的概述 以太网是一种计算机局域网技术。 从硬件的角度来说&#x…

祝贺十九岁的美创,一天拿了5个奖!

今天,和19岁的美创一起数奖🥇🥇🥇 刚刚,北京、杭州两地接连传来好消息—— 北京 被誉为中国IT业界延续时间最长的年度盛会——由赛迪顾问主办的2024年IT市场年会于今日隆重召开,备受瞩目的“首届IT创新大赛…

一份超详细的鸿蒙开发面经分享!上百道鸿蒙经典面试题整理~

鸿蒙(HarmonyOS)作为华为公司自主研发的全场景分布式操作系统,受到了广泛关注。 在面试中,面试官往往会关注申请人的技术能力、项目经验以及解决问题的能力。 下面是一些关于鸿蒙开发具有3年工作经验的面试题及其相关问答&#…

SpringBoot框架——8.MybatisPlus常见用法(常用注解+内置方法+分页查询)

1.MybatisPlus常用注解: 1.1 当数据库、表名和字段名和实体类完全一致时无需加注解,不一致时: TableName指定库名 TableId指定表名 TableField指定字段名 1.2 自增主键: TableId(typeIdType.AUTO) private Long id; 1.3 实体类中属…
最新文章