首页 > 编程语言 >一文搞懂银行家算法

一文搞懂银行家算法

时间:2024-07-21 15:24:49浏览次数:19  
标签:一文 int numberOfResources 算法 ++ 死锁 进程 搞懂 资源

在学操作系统的时候,了解到死锁问题,今天在学习并发编程时,也遇到了死锁,在了解了死锁的原因后,遇到一个经典的算法——银行家算法,这是一种避免死锁的算法。在学习完后,我决定总结一下银行家算法的核心思想。

什么是死锁?

死锁是指在计算机系统中,多个进程或线程因竞争资源或互相等待而陷入的一种永久阻塞的状态。具体来说,死锁发生在以下四个条件同时满足的情况下:

  1. 互斥条件:某些资源在同一时间只能被一个进程使用。如果其他进程请求该资源,则必须等待。

  2. 占有且等待条件:一个进程已经持有至少一个资源,但又请求新的资源,而新的资源正被其他进程持有,因此进程必须等待。

  3. 不剥夺条件:资源不能被强行从一个进程中剥夺,必须由持有它的进程主动释放。

  4. 循环等待条件:存在一个进程链,使得每个进程都在等待链中的下一个进程所持有的资源,形成一个环形等待链。

 银行家算法

计算机科学家艾兹赫尔·戴克斯特拉(Edsger Dijkstra)于1965年提出了银行家算法(Banker's Algorithm)。该算法模拟银行家在向客户贷款时的决策过程,确保系统在资源分配过程中始终处于安全状态。

先看安全状态

到底什么是安全呢?先看一个情景:

系统共有12台磁带机,然后有3个进程,P1,P2,P3

进程最大需求已分配可用
P1105
P242
P392

问题:操作系统该怎么分配呢?

除了已分配的5+2+2=9,还剩12-9=3,这3个磁带机如果给P1,完了,彻底完了,现在P1占有5+3=8台,操作系统一台不剩了,但是,P1,P2,P3,还在向操作系统申请,但是刚刚已经分配完了,这样3个进程都一直申请,由于无法结束,谁也无法释放资源,所以进入死锁状态了,这个锅得是操作系统来背,我们可以理所当然的想到,操作系统剩下的3台,应给P2两台,P2需要4台,已经有两台了,再来2台就能结束,并还操作系统4台,这样操作系统就有1+4=5台了,接下来给P1,P1顺利结束,然后给P3,P3顺利结束。问题就在分配上,通过分配就能避免死锁问题。

安全状态

安全状态是指系统能够按某种顺序为每一个进程分配资源,使得每个进程都能够顺利完成其工作并释放资源。换句话说,在安全状态下,系统总能找到一个进程序列,使得每个进程在其需求的资源得到满足后,都能顺利执行并释放资源,从而使其他进程也能获得资源并完成任务。

不安全状态

不安全状态是指系统当前无法保证一定能找到一个进程序列,使得所有进程都能够顺利完成其工作。不安全状态不一定会导致死锁,但存在进入死锁的风险。如果系统处于不安全状态,并不意味着系统已经死锁,只是说在某些资源分配情况下,系统可能会进入死锁状态。

再看银行家算法

这里大概明白银行家算法怎么来的了,就是通过算法,保证资源分配是安全的,从而避免死锁。

基本概念

  1. 资源类型和实例:系统中有多种资源类型,每种资源类型有若干实例。例如,资源类型R1有10个实例,资源类型R2有5个实例等。

  2. 进程:系统中有若干进程,每个进程需要一定数量的资源来完成其任务。

  3. 分配矩阵(Allocation):当前已经分配给各个进程的资源数量。

  4. 需求矩阵(Need):每个进程还需要的资源数量,等于其最大需求减去已分配的资源。

  5. 可用矩阵(Available):统当前可用的各类资源数量。

工作原理

  1. 安全性检查

    系统计算当前状态是否安全,即是否存在一种进程执行顺序,使得每个进程都能顺利完成并释放其占用的资源。
  2. 资源请求处理

    当进程请求资源时,系统假设分配这些资源,并再次进行安全性检查。如果分配资源后系统仍然处于安全状态,则分配资源;否则,拒绝该请求,进程继续等待。

具体步骤 

  1. 初始化

    • 创建并初始化分配矩阵、需求矩阵和可用矩阵。
  2. 资源请求

    • 进程发出资源请求。
  3. 试探性分配

    • 假设将请求的资源分配给进程,更新分配矩阵、需求矩阵和可用矩阵。
  4. 安全性检查

    • 检查假设分配后系统是否处于安全状态。如果是,正式分配资源;否则,恢复状态,拒绝请求。
示例

假设系统有三种资源类型:A、B、C,它们的实例数分别为10、5、7。系统有五个进程:P0, P1, P2, P3, P4。给出分配矩阵、最大需求矩阵和可用矩阵如下:

  • 分配矩阵(Allocation):

    A B C
    0 1 0
    2 0 0
    3 0 2
    2 1 1
    0 0 2
    
  • 最大需求矩阵(Max):

    A B C
    7 5 3
    3 2 2
    9 0 2
    2 2 2
    4 3 3
    
  • 可用矩阵(Available):

    A B C
    3 3 2

理解上的注意点 

在开始我提到了并发编程,这里指的是线程死锁,那银行家算法解决的是进程死锁还是线程死锁呢?

银行家算法主要用于解决进程死锁问题,而不是特定的线程死锁问题。虽然进程和线程在许多方面有相似之处,特别是在资源分配和同步方面,但是银行家算法是设计用来在操作系统的层面上管理资源分配,从而避免进程之间的死锁。

在多线程编程中,线程死锁也是一个重要问题,但通常解决线程死锁的方法更多是依赖于锁机制的设计和使用,如避免嵌套锁定、使用超时锁定机制或设计无锁算法等。银行家算法的思想也可以在多线程环境中应用,但其主要用途还是在进程资源分配和调度中,确保系统不会因为资源分配不当而进入死锁状态。

手动实现(模拟)

这是编程实现,有兴趣的朋友可以试试

import java.util.Scanner;

public class BankersAlgorithm {

    private int numberOfProcesses;
    private int numberOfResources;
    private int[] available;
    private int[][] maximum;
    private int[][] allocation;
    private int[][] need;

    public BankersAlgorithm(int numberOfProcesses, int numberOfResources) {
        this.numberOfProcesses = numberOfProcesses;
        this.numberOfResources = numberOfResources;
        available = new int[numberOfResources];
        maximum = new int[numberOfProcesses][numberOfResources];
        allocation = new int[numberOfProcesses][numberOfResources];
        need = new int[numberOfProcesses][numberOfResources];
    }

    public void input() {
        Scanner scanner = new Scanner(System.in);

        System.out.println("输入可用资源:");
        for (int i = 0; i < numberOfResources; i++) {
            available[i] = scanner.nextInt();
        }

        System.out.println("输入最大需求矩阵:");
        for (int i = 0; i < numberOfProcesses; i++) {
            for (int j = 0; j < numberOfResources; j++) {
                maximum[i][j] = scanner.nextInt();
            }
        }

        System.out.println("输入分配矩阵:");
        for (int i = 0; i < numberOfProcesses; i++) {
            for (int j = 0; j < numberOfResources; j++) {
                allocation[i][j] = scanner.nextInt();
            }
        }

        calculateNeed();
    }

    private void calculateNeed() {
        for (int i = 0; i < numberOfProcesses; i++) {
            for (int j = 0; j < numberOfResources; j++) {
                need[i][j] = maximum[i][j] - allocation[i][j];
            }
        }
    }

    public boolean isSafe() {
        boolean[] finish = new boolean[numberOfProcesses];
        int[] work = available.clone();

        while (true) {
            boolean found = false;

            for (int i = 0; i < numberOfProcesses; i++) {
                if (!finish[i]) {
                    boolean canAllocate = true;
                    for (int j = 0; j < numberOfResources; j++) {
                        if (need[i][j] > work[j]) {
                            canAllocate = false;
                            break;
                        }
                    }

                    if (canAllocate) {
                        for (int j = 0; j < numberOfResources; j++) {
                            work[j] += allocation[i][j];
                        }
                        finish[i] = true;
                        found = true;
                    }
                }
            }

            if (!found) {
                break;
            }
        }

        for (boolean b : finish) {
            if (!b) {
                return false;
            }
        }

        return true;
    }

    public boolean requestResources(int process, int[] request) {
        for (int i = 0; i < numberOfResources; i++) {
            if (request[i] > need[process][i]) {
                System.out.println("请求的资源超过需要的资源。");
                return false;
            }
            if (request[i] > available[i]) {
                System.out.println("请求的资源超过可用的资源。");
                return false;
            }
        }

        for (int i = 0; i < numberOfResources; i++) {
            available[i] -= request[i];
            allocation[process][i] += request[i];
            need[process][i] -= request[i];
        }

        if (!isSafe()) {
            for (int i = 0; i < numberOfResources; i++) {
                available[i] += request[i];
                allocation[process][i] -= request[i];
                need[process][i] += request[i];
            }
            System.out.println("系统进入不安全状态,资源请求被拒绝。");
            return false;
        }

        return true;
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        System.out.println("输入进程数量和资源种类数量:");
        int numberOfProcesses = scanner.nextInt();
        int numberOfResources = scanner.nextInt();

        BankersAlgorithm ba = new BankersAlgorithm(numberOfProcesses, numberOfResources);
        ba.input();

        if (ba.isSafe()) {
            System.out.println("系统处于安全状态。");
        } else {
            System.out.println("系统处于不安全状态。");
        }

        System.out.println("输入请求的进程编号和资源请求向量:");
        int process = scanner.nextInt();
        int[] request = new int[numberOfResources];
        for (int i = 0; i < numberOfResources; i++) {
            request[i] = scanner.nextInt();
        }

        if (ba.requestResources(process, request)) {
            System.out.println("资源请求成功。");
        } else {
            System.out.println("资源请求失败。");
        }
    }
}

标签:一文,int,numberOfResources,算法,++,死锁,进程,搞懂,资源
From: https://blog.csdn.net/m0_62056231/article/details/140508078

相关文章

  • 代码随想录算法训练营第35天 | 动态规划1:509.斐波那契数、70.爬楼梯、746.使用最小花
    代码随想录算法训练营第35天|动态规划理论基础https://programmercarl.com/动态规划理论基础.html#算法公开课509.斐波那契数https://leetcode.cn/problems/fibonacci-number/submissions/548309803/代码随想录https://programmercarl.com/0509.斐波那契数.html#算法公开......
  • 常见的排序算法——堆排序(四)
    本文记述了针对堆排序同时实施减少数据交换和Floyd方法的一份参考实现代码,并在说明了算法的性能后用随机数据进行了验证。◆思想减少数据交换的操作,请参考堆排序(二);Floyd方法,请参考堆排序(三)(此处略去详细说明)。◆实现排序代码采用《算法(第4版)》的“排序算法类模板”实现。(......
  • 一文揭开JDK21虚拟线程的神秘面纱
    虚拟线程快速体验环境:JDK21+IDEApublicstaticvoidmain(String[]args){try(varexecutor=Executors.newVirtualThreadPerTaskExecutor()){IntStream.range(0,10_000).forEach(i->{executor.submit(()->{Thread.sle......
  • 一文揭开JDK21虚拟线程的神秘面纱
    虚拟线程快速体验环境:JDK21+IDEApublicstaticvoidmain(String[]args){try(varexecutor=Executors.newVirtualThreadPerTaskExecutor()){IntStream.range(0,10_000).forEach(i->{executor.submit(()->{Threa......
  • 【软考】数据结构与算法基础 - 数组和链表
    一、数组和链表的区别(很简单,但是很常考,记得要回答全面)什么是数组:C++语言中,可以用数组,处理一组数据类型相同的数据,不可以动态定义数组的大小(使用前,必须指定大小。)在实际应用中,用户使用数组之前,无法确定数组的大小只能够将数组定义成足够大小,多余出来空间可能不被使用,......
  • 常见的排序算法——堆排序(三)
    本文记述了针对堆排序实施Floyd方法的基本思想和一份参考实现代码,并在说明了算法的性能后用随机数据进行了验证。◆思想“大多数在下沉排序期间重新插入堆的元素会被直接加入到堆底。Floyd在1964年观察发现,我们正好可以通过免去检查元素是否到达正确位置来节省时间。”(引......
  • 【故障诊断】基于斑马优化算法ZOA优化长短记忆网络LSTM实现故障诊断附matlab代码
    %导入数据集load(‘fault_diagnosis_data.mat’);%假设故障诊断数据保存在fault_diagnosis_data.mat文件中%数据预处理%这里省略了数据预处理的步骤,包括数据归一化、特征提取等%划分训练集和测试集train_ratio=0.8;%训练集占总数据的比例train_size=round......
  • 【独家首发】Matlab实现淘金优化算法GRO优化Transformer-LSTM实现负荷数据回归预测
    %导入数据集load(‘load_data.mat’);%假设负荷数据保存在load_data.mat文件中%数据预处理%这里省略了数据预处理的步骤,包括数据归一化、特征提取等%构建Transformer-LSTM模型model=create_transformer_lstm_model();%自定义创建Transformer-LSTM模型的函数......
  • 【独家首发】Matlab实现狮群优化算法LSO优化Transformer-LSTM实现负荷数据回归预测
    %导入数据集load(‘load_data.mat’);%假设负荷数据保存在load_data.mat文件中%数据预处理%这里省略了数据预处理的步骤,包括数据归一化、特征提取等%构建Transformer-LSTM模型model=create_transformer_lstm_model();%自定义创建Transformer-LSTM模型的函数......
  • 一文了解MySQL的子查询
    文章目录☃️1.需求分析与问题解决❄️❄️1.1实际问题❄️❄️1.2子查询的基本使用❄️❄️1.3子查询的分类☃️2.单行子查询❄️❄️2.1单行比较操作符❄️❄️2.2代码示例❄️❄️2.3HAVING中的子查询❄️❄️2.4注意的问题☃️3.多行子查询❄️❄️3.1多行比较操作符❄️❄️3.2代码示例☃️4.相......