实验目的:
通过实验进一步了解银行家算法及其蕴含的思想,算法能力复健。
预期效果:
输入:
首先输入1-9之间的数字,表示请求端数量。
再输入1-9之间的数字,表示资源种类数量。
再输入请求端与资源种类构成的矩阵,体现每个请求端对于每种资源的最大需求数量。
再输入请求端与资源种类构成的矩阵,体现已经分配给各个请求端的各种资源数量。
再输入当前各个资源的可用数量。
输出:
输出当前系统是否安全,并给出一条安全的调度路径。
实验背景:
银行家算法(Banker's Algorithm)是一个避免死锁(Deadlock)的著名算法,是由艾兹格·迪杰斯特拉(没错,又是他,动态规划的神!!!)在1965年为T.H.E系统设计的一种避免死锁产生的算法。它以银行借贷系统的分配策略为基础,判断并保证系统的安全运行。
银行家算法通过在资源分配之前,计算出当前系统是否安全,来确保系统运行中不会出现死锁问题。
实验思路:
根据Allocation与Max矩阵计算出Need矩阵。
再通过遍历试探的方法,寻找一条安全的分配路径。
测试用例:
Allocation(A B C) | Max(A B C) | |
p0 | 0 1 0 | 7 5 3 |
p1 | 2 0 0 | 3 2 2 |
p2 | 3 0 2 | 9 0 2 |
p3 | 2 1 1 | 2 2 2 |
p4 | 0 0 2 | 4 3 3 |
Available: [3 3 2]
实验代码:
结合其中的中文字符串与代码注释可以理解下面的代码,本人算法能力不强,对以下代码有异议或者建议可以向我提出。
import java.util.Arrays;
import java.util.Scanner;
public class BankersAlgorithm {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.print("请输入请求端数量\n>> ");
int clientCount = Integer.parseInt(sc.nextLine());
System.out.print("请输入资源种类数量\n>> ");
int resourceTypeCount = Integer.parseInt(sc.nextLine());
int[][] max = new int[clientCount][resourceTypeCount];
System.out.println("请输入最大需求矩阵:");
for (int i=0; i<clientCount; i++) {
String[] resourceMaxString = sc.nextLine().split(" ");
for (int j=0; j<resourceTypeCount; j++) {
max[i][j] = Integer.parseInt(resourceMaxString[j]);
}
}
showMatrix(max);
int[][] allocation = new int[clientCount][resourceTypeCount];
System.out.println("请输入已分配矩阵:");
for (int i=0; i<clientCount; i++) {
String[] resourceAllocationString = sc.nextLine().split(" ");
for (int j=0; j<resourceTypeCount; j++) {
allocation[i][j] = Integer.parseInt(resourceAllocationString[j]);
}
}
showMatrix(allocation);
int[] available = new int[resourceTypeCount];
System.out.println("请输入当前各种资源可用的数量:");
String[] resourceAvailableString = sc.nextLine().split(" ");
for (int j=0; j<resourceTypeCount; j++) {
available[j] = Integer.parseInt(resourceAvailableString[j]);
}
System.out.println("当前可用资源数量"+Arrays.toString(available));
System.out.println("------- 数据初始化完成 -------");
int[][] need = new int[clientCount][resourceTypeCount];
for (int i=0; i<clientCount; i++) {
for (int j=0; j<resourceTypeCount; j++) {
need[i][j] = max[i][j] - allocation[i][j];
}
}
System.out.println("需求矩阵如下:");
showMatrix(need);
bankersAlgorithm(allocation, need, available);
}
private static void bankersAlgorithm(int[][] allocation, int[][] need, int[] available) {
int clientCount = allocation.length;
int resourceTypeCount = available.length;
boolean[] executed = new boolean[clientCount]; // 记录每个进程是否已执行
int[] work = Arrays.copyOf(available, resourceTypeCount); // 记录每种资源的可用数量
int count = 0;
String safeQ = ""; // 保存安全执行的进程序列
// 遍历所有进程,直到所有进程都执行完毕或无法找到可执行的进程
while (count < clientCount) {
boolean found = false;
// 遍历每个进程,检查其资源需求是否小于等于可用资源
for (int i = 0; i < clientCount; i++) {
if (!executed[i] && judge(need[i], work)) {
// 如果找到可执行的进程,则标记它已执行,释放其已分配资源并更新可用资源
executed[i] = true;
for (int j = 0; j < resourceTypeCount; j++) {
work[j] += allocation[i][j];
}
found = true;
count++;
safeQ += " -> P" + i;
}
}
// 如果没有找到可执行的进程,说明系统不在安全状态,退出循环
if (!found) {
break;
}
}
// 根据是否找到安全序列输出结果
if (count == clientCount) {
System.out.println("系统处于安全状态。");
System.out.println("安全序列:" + safeQ.substring(4)); // 去掉开头的 "-> "
} else {
System.out.println("系统不在安全状态。");
}
}
// 判断当前进程的资源需求是否小于等于可用资源
private static boolean judge(int[] aNeed, int[] available) {
for (int i = 0; i < aNeed.length; i++) {
if (aNeed[i]>available[i]) {
return false;
}
}
return true;
}
private static void showMatrix(int[][] matrix) {
for (int i = 0; i < matrix.length; i++) {
System.out.println(i+" "+Arrays.toString(matrix[i]));
}
}
}
测试结果:
通过测试,得到预期结果。
题外话:这样看来银行家算法确实是一个很好的避免死锁的方案,但是其可行性不足,该算法最重要的一个前提条件是要提前知道所有进程即将申请的最大资源数量,这几乎是不可能达成的事,另外还存在着静态资源分配和代价高的问题。当然了,作为一种动态规划算法,它其中包含的算法思维却是很有意思,很值得我们学习的。