锁策略和死锁问题

锁策略

  • 乐观锁 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.mzph.cn/pingmian/319.shtml

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

相关文章

Java配置环境变量的过程

第一步:先找到你下载java的文件夹。 第二步:点击它进入看到新的文件夹继续点击。 第三步:点击bin文件。 第四步:点进去bin文件之后复制上述文件的地址。 第五步:回到你的电脑位置右键鼠标点击空白位置出现属性点进去 第…

设计模式胡咧咧之策略工厂实现导入导出

策略模式(Strategy Pattern) 定义: 定义了一组算法,将每个算法都封装起来,并且使它们之间可以互换。 本质: 分离算法,选择实现 应用场景 何时使用 一个系统有许多类,而区分他们的只是他们直接…

Ubuntu22.04.4 - MySQL8 - 笔记

MySQL8在Ubuntu22.04.4环境下的笔记 一、安装 sudo apt update sudo apt install mysql-server sudo systemctl status mysql 二、卸载 sudo apt remove --purge mysql-* sudo apt autoremove dpkg --list | grep mysql查询是否还存在相关的依赖组件 dpkg -l |grep ^rc|awk {p…

复合机器人在磁钢上下料中的应用及其优势分析

复合机器人是一种集成了移动机器人和工业机器人功能的设备,其独特之处在于拥有“手、脚、眼、脑”的综合能力,从而实现了更高的灵活性和操作效率。在磁钢上下料的应用场景中,复合机器人能够发挥显著的优势。 首先,复合机器人可以根…

Photomator 3.3.5 (macOS Universal) - 照片编辑软件

Photomator 3.3.5 (macOS Universal) - 照片编辑软件 适用于 Mac、iPhone 和 iPad 的终极照片编辑器 请访问原文链接:https://sysin.org/blog/photomator/,查看最新版。原创作品,转载请保留出处。 作者主页:sysin.org Photomat…

Nodejs 第六十五章(SDL单设备登录)

单设备登录 SDL(Single Device Login)是一种单设备登录的机制,它允许用户在同一时间只能在一个设备上登录,当用户在其他设备上登录时,之前登录的设备会被挤下线。 应用场景 视频影音,防止一个账号共享&a…

关于项目中加入线程池,导致JVM性能急剧下降的问题

在项目中,为了提高新系统服务的并发处理能力,我在项目中加入了线程池。 由于原来的项目是单线程的一个服务,或者说是一个少线程的服务. 所以公司前辈,将jvm的TLAB 参数配置进行关闭了。 TLAB(Thread Local Allocation …

计算机网络 -- 网络编程基础

一 学习准备 1.1 IP地址 在 前文中我们提到过: IP 是全球网络的基础,使用 IP 地址来标识公网环境下主机的唯一性,我们可以根据 目的IP地址 进行跨路由器的远端通信。 但是我们也提到了,通过 IP 地址,只能寻找到目标主机&#xff…

视频质量度量VQM算法详细介绍

视频质量评价 视频质量评价(Video Quality Assessment,VQA)是指通过主观、客观的方式对视频图像的内容、画质等,进行感知、衡量与评价。 ITU definations subjective assessment: the determination of the quality or impairment of programme-like pictures presented…

MASA Framework-原理分析(6)

MASA Framework的原理分析主要涉及到其核心组件、架构设计和运行机制等多个方面。以下是对MASA Framework原理的深入解析: 1. 核心组件与功能 MASA Framework的核心组件包括服务容器、数据存储、消息队列以及监控和管理工具等。这些组件共同协作,为开发…

桌面应用框架概论

目录 桌面应用框架概论 1.介绍 2.优缺点 3.总结 桌面应用框架概论 1.介绍 桌面应用框架种类繁多,以下是一些主要的框架: Electron:这是一个用于创建跨平台桌面应用的开源框架。它允许开发者使用Web技术(HTML、CSS和JavaScript)来构建桌面应用程序,使得桌面应用的开…

字符串的一些有趣案例

文章目录 简介删除文本中的括号和其中的文本 简介 分享python 字符串操作的一些有趣的案例: 正则表达式format … 删除文本中的括号和其中的文本 存在中英文括号混用,故要考虑到所有可能到情况 import re def remove_all_parentheses(text): # 正…

MongoDB聚合运算符:$replaceOne

MongoDB聚合运算符:$replaceOne 文章目录 MongoDB聚合运算符:$replaceOne语法参数字段说明 使用$replaceOne 与 Null$replaceOne 和排序规则$replaceOne 和 Unicode 规范化 举例 $replaceOne聚合运算在输入的字符串中搜索目标字符串,并使用指…

【代码随想录】【动态规划】day48:打家劫舍

打家劫舍1 def rob(self, nums):""":type nums: List[int]:rtype: int"""# 分为两个情况,偷还是不偷,# dp[i]为考虑到第i个房间时的最大值if len(nums) 0: # 如果没有房屋,返回0return 0if len(nums) 1: #…

【 书生·浦语大模型实战营】作业(五):LMDeploy 量化部署

【 书生浦语大模型实战营】作业(五):LMDeploy 量化部署 🎉AI学习星球推荐: GoAI的学习社区 知识星球是一个致力于提供《机器学习 | 深度学习 | CV | NLP | 大模型 | 多模态 | AIGC 》各个最新AI方向综述、论文等成体系…

Linux debian gdb dump

1.开发背景 记录 debian 下应用程序崩溃调试方法 2.开发需求 程序越界可以定位到越界的位置附近 3.开发环境 debian 操作系统,如果不支持需要查看是否存在对应的可执行文件 4.实现步骤 4.1 设置 dump 输出大小 ulimit -c unlimited # 设置输出大小 生成core 文…

【QT+OpenCV】车牌号检测 学习记录 遇到的问题

【QTOpenCV】车牌号检测 学习记录 首先在QT里面配置好OpenCV .pro文件中加入: INCLUDEPATH G:/opencv/build/include LIBS -L"G:/opencv/build/x64/vc14/lib"\-lopencv_core \-lopencv_imgproc \-lopencv_highgui \-lopencv_ml \-lopencv_video \-lo.c…

Linux下SPI设备驱动实验:使用内核提供的读写SPI设备中的数据的函数

一. 简介 前面文章的学习,已经实现了 读写SPI设备中数据的功能。文章如下: Linux下SPI设备驱动实验:验证读写SPI设备中数据的函数功能-CSDN博客 本文来使用内核提供的读写SPI设备中的数据的API函数,来实现读写SPI设备中数据。 …

JSON文件初识

JSON(JavaScript Object Notation)是一种用于存储和交换数据的轻量级数据格式。它独立于任何编程语言,因此可以在各种语言环境中使用,包括网页、服务器、应用程序等。JSON的简洁性、可读性以及易于解析的特性,使其在网…

HTML5+JavaScript实现本地视频/音频播放器

HTML5JavaScript实现本地视频/音频播放器 HTML5 提供了本地视频和音频播放器的支持&#xff0c;通过 <video> 和 <audio> 标签&#xff0c;这些标签支持多种媒体格式&#xff0c;并且可以通过 JavaScript 进行控制&#xff0c;实现功能比较完整的本地视频音频播放器…