经典面试题-死锁

news2024/11/16 5:30:49

目录

1.什么是死锁?

2.形成死锁的四个必要条件

3.死锁的三种情况

   第一种情况:

    举例:

     举例:

第二种情况:两个线程 两把锁

     举例:

第三种情况:N个线程 M把锁

哲学家进餐问题


1.什么是死锁?

        死锁是指在并发系统中,两个或多个进程(或线程)互相等待对方所占有的资源而无法继续执行的情况。这种情况下,每个进程都在等待其他进程释放资源,导致所有进程都无法向前推进。

2.形成死锁的四个必要条件

         1.互斥使用,获取锁的过程是互斥的。一个线程拿到了一把锁,另一个线程也想获取这把锁,就需要阻塞等待。

        2.不可抢占。一个线程拿到了锁之后,只能主动解锁,不能让别的线程强行把锁抢走。

        3.请求保持。一个线程拿到了锁A之后,在持有A的前提下,尝试获取B。

         4.循环等待:存在一个进程链,每个进程都在等待下一个进程所占有的资源。

3.死锁的三种情况

   第一种情况:

如果锁是不可进重入锁,并且一个线程对这把锁加锁了两次,那么它就会出现死锁的情况。

如果不是不可进重入锁。会出现下面的情况

    举例:

package 多线程;
//死锁
public class ThreadDemo15 {
    public static void main(String[] args) {
        Object locker =new Object();
        Thread t = new Thread(()-> {
            synchronized (locker) {
                synchronized (locker) {//当前由于事同一个线程,此时锁对象,就知道第二次加锁的线程,就是持有锁的线程。第二次操作会直接放行。
                    System.out.println("hello");
                }
            }//在这里解锁
        });
        t.start();
    }
}

它就会打印一个hello。 

 如果是不可重进入锁

     举例:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class DeadlockExample {
    private static Lock lock = new ReentrantLock();

    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            lock.lock();
            System.out.println("Thread is holding the lock");

            // 尝试再次获取锁,会导致死锁
            lock.lock(); 

            System.out.println("This line will not be reached");
            lock.unlock();
        });

        thread.start();
    }
}

他就不会输出东西。

第二种情况:两个线程 两把锁

线程1 获取到 锁A

线程2 获取到 锁B

接下来,1 尝试获取B,2尝试获取A ,就会出现死锁。

一旦出现死锁,线程就会被卡住无法继续工作。

     举例:

package 多线程;
//死锁
public class ThreadDemo16 {
    public static void main(String[] args) {
        Object A = new Object();
        Object B = new Object();
        Thread t1 = new Thread(()->{
            synchronized (A){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //A尝试获取B,并没有释放A
                synchronized (B){
                    System.out.println("t1 拿到了B");
                }
            }
        });
        Thread t2 = new Thread(()->{
            synchronized (B){//约定加锁顺序,
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (A){
                    System.out.println("t2 拿到了A");
                }
            }
        });
        t1.start();
        t2.start();
    }
}

当我们输出结果就会发现它一直没有输出任何东西。 当t1线程持有A的锁资源时,它尝试获取B,而同时t2线程持有B的锁资源,它尝试获取A,这样两个线程相互等待对方的锁资源,导致死锁的情况发生。

         如何去解决这个问题呢,关键就在于死锁形成的四个必要条件,只要我们可以打破这四个必要条件,就不会形成死锁。这个题,我们约定好加锁的顺序的话,就不会出现死锁。

package 多线程;
//死锁
public class ThreadDemo16 {
    public static void main(String[] args) {
        Object A = new Object();
        Object B = new Object();
        Thread t1 = new Thread(()->{
            synchronized (A){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //A尝试获取B,并没有释放A
                synchronized (B){
                    System.out.println("t1 拿到了B");
                }
            }
        });
        Thread t2 = new Thread(()->{
            synchronized (A){//约定加锁顺序,
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (B){
                    System.out.println("t2 拿到了A");
                }
            }
        });
        t1.start();
        t2.start();
    }
}

        当t1线程获取到A对象的锁资源后,它会尝试获取B对象的锁资源,但是此时B对象已经被t2线程锁住了,因此t1线程会进入等待状态。当t2线程获取到B对象的锁资源后,它会尝试获取A对象的锁资源,此时A对象没有被锁住,因此t2线程可以获取到A对象的锁资源,执行完成后释放锁资源,然后t1线程才能继续执行,获取B对象的锁资源,避免了死锁的发生。 

 

第三种情况:N个线程 M把锁

哲学家进餐问题

        描述了五位哲学家围坐在一张圆桌旁,每个人面前都有一碗米饭和一只筷子。这些哲学家只能用左手和右手各拿一只筷子进食。问题是,如何安排他们的动作,使得每个哲学家都能进餐?

问题分析 :

       由问题描述我们可以知道,一共有五个哲学家,也就是五个进程;五只筷子,也就是五个临界资源;因为哲学家想要进餐,必须要同时获得左边和右边的筷子,这就是要同时进入两个临界区(使用临界资源),才可以进餐。

 问题解决:

  1. 一次只允许两个哲学家进餐,并且要求他们都拿到右手边的筷子后才能开始进食。
  2. 引入一个仲裁者,即一个额外的实体负责协调哲学家的动作,以避免死锁的发生。
  3. 使用资源分配算法,例如Dijkstra的银行家算法,来确保每个哲学家都能有足够的资源进餐。

 

    希望大家多多支持! 

 

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

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

相关文章

Docker基础语法

目录 一.docker安装 二.docker基础名词 三.docker基础命令 四.命令别名 五.数据卷 六.挂载本地目录或文件 七.Docker镜像 八.网络 一.docker安装 1.安装yum工具 yum install -y yum-utils device-mapper-persistent-data lvm2 2.安装 docker yum源 yum-config-manag…

Git的安装与配置

目录 前言 Linux-centos:下安装 Linux_ubuntu下安装 创建Git本地仓库 配置用户名和Email 前言 Git是一种版本控制器,能够让我们了解一个文件的历史,以及它的发展过程。通俗的将就是可以记录一个工程的每一次改动和版本迭代的一个管理系统…

书法AI全自动切字+识别算法2.0版发布,草书篆书行书楷书识别准确率超过90%,覆盖书法单字30万张

我们开发的业界识别最准覆盖作品最全的书法AI小程序上线了 书法AI全自动切字识别算法2.0版发布,草书篆书行书楷书识别准确率超过90%,准确率甩百度OCR一条街,覆盖书法单字30万张,遥遥领先同行 我们还可为客户提供书法AI全自动切字a…

Tensorflow2.0笔记 - tensor的合并和分割

主要记录concat,stack,unstack和split相关操作的作用 import tensorflow as tf import numpy as nptf.__version__#concat对某个维度进行连接 #假设下面的tensor0和tensor1分别表示4个班级35名同学的8门成绩和两个班级35个同学8门成绩 tensor0 tf.ones([4,35,8]) tensor1 tf…

C#,入门教程(30)——扎好程序的笼子,错误处理 try catch

上一篇: C#,入门教程(29)——修饰词静态(static)的用法详解https://blog.csdn.net/beijinghorn/article/details/124683349 程序员语录:凡程序必有错,凡有错未必改! 程序出错的原因千千万&…

基于PSO-BP神经网络的风电功率预测(MATLAB)

作品简介 :关注公众号“电击小子程高兴的MATLAB小屋”获取优惠 主要内容 该模型将粒子群算法与BP神经网络结合用于BP神经网络的训练,即优化BP网络中的连接权值和各项阈值,然后利用神经网络分布式并行处理优势、自适应学习能力以及较好的…

坚持刷题 | 二叉树的最大深度

文章目录 题目考察点代码实现实现总结扩展用迭代的方式实现二叉树最大深度可能的扩展问题 坚持刷题,老年痴呆追不上我,今天刷:二叉树的最大深度 题目 104.二叉树的最大深度 考察点 二叉树的基本实现: 能够定义二叉树节点&…

Apifox接口测试教程(一)接口测试的原理与工具

🔥 交流讨论:欢迎加入我们一起学习! 🔥 资源分享:耗时200小时精选的「软件测试」资料包 🔥 教程推荐:火遍全网的《软件测试》教程 📢欢迎点赞 👍 收藏 ⭐留言 &#x1…

性能优化-OpenCL 介绍

「发表于知乎专栏《移动端算法优化》」 本文首先对 GPU 进行了概述,然后着重地对移动端的 GPU 进行了分析,随后我们又详细地介绍了 OpenCL 的背景知识和 OpenCL 的四大编程模型。希望能帮助大家更好地进行移动端高性能代码的开发。 🎬个人简介…

git本地分支的合并/切换分支时遇到的问题

目录 第一章、本地分支的切换测试1.1)切换之前的master分支下文件内容1.2)切换到develop分支后修改文件1.3)切回master分支出现报错: 第二章、解决方式2.1)方式1:commit提交修改2.2)方式2&#…

柔性数组和C语言内存划分

柔性数组和C语言内存划分 1. 柔性数组1.1 柔性数组的特点:1.2 柔性数组的使用1.3 柔性数组的优势 2. 总结C/C中程序内存区域划分 1. 柔性数组 也许你从来没有听说过柔性数组(flexible array)这个概念,但是它确实是存在的。 C99 中&#xff…

解决Git添加.gitignore文件后不生效的问题

1. 问题描述 如上图所示,在已存在.gitignore文件且已经提交过的Git管理的项目中,其中.class、.jar文件以及.idea目录内的内容全部都还是被Git管理了,可见.gitignore文件并没有生效。 2. 原因发现 .gitignore文件只能作用于 Untracked Files…

vue+elementUI el-select 中 没有加clearable出现一个或者多个×清除图标问题

1、现象:下方截图多清除图标了 2、在全局common.scss文件中加一个下方的全局样式noClear 3、在多清除图标的组件上层div加noClear样式 4、清除图标去除成功

iOS应用程序混淆加固原理及逆向工具介绍

概要 本文将介绍iOS应用程序混淆加固的原理和常见的加固类型,以及介绍一些常用的逆向工具。同时还会介绍一种代码虚拟化技术,用于进一步保护应用程序不被逆向分析。 引言 移动应用程序开发面临着越来越严峻的安全挑战,特别是在越狱设备上…

k8s1.27.2版本二进制高可用集群部署

文章目录 环境软件版本服务器系统初始化设置关于etcd签名证书etcd集群部署负载均衡器组件安装设置关于k8s自签证书自签CAkube-apiserver 自签证书kube-controller-manager自签证书kube-scheduler自签证书kube-proxy 自签证书admin 自签证书 控制平面节点组件部署**部署kube-api…

Uni-App三甲医院、医保定点三甲医院在线预约挂号系统源码

医院在线预约挂号系统是一种方便患者预约挂号的系统,患者可以通过该系统进行预约挂号,省去了到医院现场排队等待的时间,提高了就诊效率。随着医院信息化水平的不断发展,医院在线预约挂号管理系统已成为医院管理中不可或缺的一部分…

如何在Linux部署JumpServer堡垒机并实现远程访问本地服务

文章目录 前言1. 安装Jump server2. 本地访问jump server3. 安装 cpolar内网穿透软件4. 配置Jump server公网访问地址5. 公网远程访问Jump server6. 固定Jump server公网地址 前言 JumpServer 是广受欢迎的开源堡垒机,是符合 4A 规范的专业运维安全审计系统。JumpS…

KAGGLE · GETTING STARTED CODE COMPETITION 图像风格迁移 示例代码阅读

本博文阅读的代码来自于I’m Something of a Painter Myself | Kaggle倾情推荐: Monet CycleGAN Tutorial | Kaggle 数据集说明 I’m Something of a Painter Myself | Kaggle Files monet_jpg - 300 Monet paintings sized 256x256 in JPEG formatmonet_tfrec -…

Redis相关面试题大全

📕作者简介: 过去日记,致力于Java、GoLang,Rust等多种编程语言,热爱技术,喜欢游戏的博主。 📗本文收录于java面试题系列,大家有兴趣的可以看一看 📘相关专栏Rust初阶教程、go语言基…

如何快速制作动态gif图?制作gif动图就这么简单

静图和动图是图像的两种不同形式。静图是一张静止不动的图片,没有任何动作或变化。而动图则是由一系列静止的图像组成,通过快速连续播放这些图像,可以形成看起来像是有动作的效果。简单来说,静图是静止的,而动图是具有…