在说银行家算法之前,大家要先理解一个概念:死锁
什么是死锁?为什么需要避免死锁?
死锁是指在一个系统中,两个或多个进程因为争夺资源而无法继续执行的状态。在死锁状态下,进程无法释放所持有的资源,也无法获取所需的资源,导致系统无法继续运行 死锁需要被避免,因为它会导致系统的停滞和资源浪费。当发生死锁时,系统无法继续处理新的任务,而已经分配给进程的资源也无法被其他进程使用,从而降低了系统的效率和可用性。 |
理解了死锁的概念,现在我们来说什么是银行家算法?
银行家算法是一种用于避免死锁的资源分配算法。它通过模拟资源的分配和释放过程,来判断系统是否会进入不安全状态。它基于以下几个假设:
银行家算法的步骤如下:
银行家算法的核心思想是通过模拟资源的分配和释放过程,来判断系统是否会进入不安全状态。它通过检查每个分配情况下系统是否安全,来避免死锁的发生。如果找到一个安全序列,则说明系统是安全的,可以进行资源的分配;如果找不到安全序列,则说明系统是不安全的,需要等待资源的释放或请求的撤销。 |
所以,银行家算法跟死锁是息息相关的,下面通过一个例题更清晰的理解银行家算法
假设有以下情景:有4个进程(P0、P1、P2、P3)和3种资源(R0、R1、R2)。每个进程对资源的最大需求如下: P0: 7 5 3 当前系统的资源分配情况如下: 已分配资源: 可用资源: 现在,我们来使用银行家算法判断系统是否是安全的。 |
首先,计算每个进程的需求矩阵,需求矩阵是最大资源矩阵减去分配矩阵: 需求矩阵: 接下来,我们初始化工作向量和完成标记数组: 工作向量:3 3 2 然后,进入循环,直到找到一个安全序列或所有进程都完成。 在第一次循环中,找到一个满足条件的进程,即进程的需求小于等于工作向量中的可用资源数量,并且进程未完成。根据银行家算法的原理,我们选择进程P1。 将P1加入安全序列,更新工作向量和完成标记数组: 在第二次循环中,找到一个满足条件的进程,即进程的需求小于等于工作向量中的可用资源数量,并且进程未完成。根据银行家算法的原理,我们选择进程P3。 将P3加入安全序列,更新工作向量和完成标记数组: 在第三次循环中,找到一个满足条件的进程,即进程的需求小于等于工作向量中的可用资源数量,并且进程未完成。根据银行家算法的原理,我们选择进程P0。 将P0加入安全序列,更新工作向量和完成标记数组: 在第四次循环中,找到一个满足条件的进程,即进程的需求小于等于工作向量中的可用资源数量,并且进程未完成。根据银行家算法的原理,我们选择进程P2。 将P2加入安全序列,更新工作向量和完成标记数组: 在第五次循环中,找不到满足条件的进程,因为P0、P1、P2、P3都已经完成。此时,所有进程都完成,说明系统是安全的。 因此,根据银行家算法的判断,系统是安全的,可以进行资源的分配。 |
通过这个希望你能更加深刻认识到银行家算法的原理及死锁,如何避免死锁,安全序列等概念。
最后,我们通过java编程模拟实现银行家算法,一共三个类,分别是Process.java、使用银行家算法的Java程序的主类Main.java、以及主体的BankerAlgorithm.java
Process类可以用于创建表示进程的对象,并获取和设置进程的属性值
//创建表示进程的对象,并获取和设置进程的属性值
public class Process {
private int id;//进程的唯一标识符
private int[] requirements;//进程对资源的需求
private int allocated;//已分配给进程的资源数量
public Process(int id, int[] requirements) {//初始化进程对象。接受进程的id和需求资源数组作为参数,
this.id = id;
this.requirements = requirements;
this.allocated = 0;//并将allocated初始化为0
}
public int getId() {
return id;
}
public int[] getRequirements() {
return requirements;
}
public int getAllocated() {
return allocated;
}
public void setAllocated(int[] newAllocated) {//设置已分配的资源数量。接受一个整数数组作为参数,并将数组的第一个元素赋值给allocated
this.allocated = newAllocated[0];
}
}
Main类是整个程序的入口点,在里面我们创建了一个BankerAlgorithm对象,并将最大资源数量和进程列表作为参数传递给构造函数。然后,调用isSafe()方法来检测系统是否安全,并将结果存储在布尔变量isSafe中。最后,使用System.out.println()方法将结果输出到控制台
import java.util.List;
import java.util.ArrayList;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 获取最大资源数量
System.out.print("请输入最大资源数量:");
int maxResource = scanner.nextInt();
// 创建进程列表,存储进程对象
List processes = new ArrayList<>();
// 获取进程数
System.out.print("请输入进程数量:");
int numProcesses = scanner.nextInt();
// 获取每个进程的资源分配情况,
for (int i = 1; i <= numProcesses; i++) {
System.out.print("请输入进程" + i + "的资源分配情况(处理器、内存、IO设备):");
int[] resources = new int[3];
resources[0] = scanner.nextInt(); // 处理器
resources[1] = scanner.nextInt(); // 内存
resources[2] = scanner.nextInt(); // IO设备
processes.add(new Process(i, resources));//根据输入的值创建Process对象,并将其添加到进程列表中
}
// 创建银行家算法实例
BankerAlgorithm bankerAlgorithm = new BankerAlgorithm(maxResource, processes);
// 检测系统是否安全
boolean isSafe = bankerAlgorithm.isSafe();
// 输出结果
System.out.println("系统是否安全:" + isSafe);
}
}
最后是使用银行家算法的BankerAlgorithm类
import java.util.*;
public class BankerAlgorithm {
private int maxResource; // 最大资源数量
private int[] allocation; // 分配给进程的资源数量
private int[] available; // 可用的资源数量
private Queue safeSequence; // 安全序列
private Map processMap; // 进程ID到进程对象的映射
private List processes; // 进程列表
public BankerAlgorithm(int maxResource, List processes) {
this.maxResource = maxResource;
this.processes = processes;
this.available = new int[maxResource];
this.allocation = new int[maxResource];
this.safeSequence = new LinkedList<>();
this.processMap = new HashMap<>();//将进程ID映射到进程对象
for (int i = 0; i < maxResource; i++) {
available[i] = maxResource;
}
for (Process process : processes) {
processMap.put(process.getId(), process);
}
}
public boolean isSafe() {
for (Process process : processes) {
int[] requirements = process.getRequirements();
int n = requirements.length;
boolean flag = false;
for (int i = 0; i < n; i++) {
if (available[i] >= requirements[i]) {
available[i] -= requirements[i]; // 占用资源
allocation[i] += requirements[i]; // 更新分配给进程的资源数量
flag = true; // 找到一个安全序列,记录下来并退出循环
break; // 只需要找到一个安全序列即可,不需要找到所有安全序列,因此退出循环
} else if (allocation[i] < maxResource) { // 如果当前进程已经分配了部分资源,并且还有剩余资源可供分配,那么尝试分配部分资源给该进程,以形成一个新的安全序列
int[] newAllocation = new int[n];
System.arraycopy(allocation, 0, newAllocation, 0, n); // 复制当前分配给进程的资源数量到新数组中
newAllocation[i] += available[i]; // 分配部分资源给该进程,更新新数组中该资源的数量
if (checkSafe(newAllocation)) { // 检查新的分配是否安全,如果是安全的,则记录下来并退出循环,否则继续搜索下一个安全序列或尝试分配部分资源给该进程
available[i] -= available[i]; // 占用剩余的可用资源数量,因为已经将其分配给了该进程的一部分需求,所以需要将其从可用资源数量中减去,以保持可用资源的正确性
flag = true; // 找到一个安全序列,记录下来并退出循环}
}
}
if (!flag) {
return false; // 如果没有找到安全序列,则说明系统不安全
}
}
safeSequence.offer(process); // 将当前进程添加到安全序列中
}
return true; // 所有进程都可以安全执行,则说明系统是安全的
}
private boolean checkSafe ( int[] newAllocation){
int n = newAllocation.length;
boolean flag = true;
for (int i = 0; i < n; i++) {
if (newAllocation[i] > maxResource) {
return false; // 分配给某个进程的资源数量超过了最大资源数量,因此不是安全序列
}
for (int j = 0; j < n; j++) {
if (i != j && newAllocation[i] > allocation[j]) {
flag = false; // 分配给某个进程的资源数量超过了其他某个进程的资源需求,因此不是安全序列
break; // 不需要继续检查,因为某个进程的需求得不到满足,后续的检查结果也一定不是安全的,所以退出循环
}
}
if (!flag) {
break; // 不是安全序列,退出循环
}
}
return flag; // 如果是安全序列,则返回true,否则返回false
}
}
在isSafe()方法中,当找到一个安全序列时,会将该进程添加到安全序列中。最后,如果所有进程都可以安全执行,则说明系统是安全的,返回true;否则,返回false。
checkSafe()方法。它用于检查给定的分配是否是安全序列。它首先检查分配给某个进程的资源数量是否超过了最大资源数量,如果是,则不是安全序列。然后它检查分配给某个进程的资源数量是否超过了其他某个进程的资源需求,如果是,则不是安全序列。如果都满足条件,则是安全序列。
这个算法的思想是通过模拟资源的分配和释放过程,来判断系统是否会进入不安全状态。它会遍历所有可能的资源分配情况,并检查每个分配情况下系统是否安全。如果找到一个安全序列,则说明系统是安全的;如果找不到安全序列,则说明系统是不安全的。
以上,就是整个代码。
至于完全的课程设计文档,相信大家借助这个应该很快就能完成。