哈喽,大家hao!
今天介绍银行家算法,还是老样子,用java来写,总共代码差不多600多行,分为两个类,一个是存储进程队列的信息,一个设计相关的实现方法。在写之前,大概搜了一下网上现有的银行家算法,发现大多数算法都写死了,定义资源的数组都是固定的?
感觉不太行,所以自己照着书本,断断续续的,大概写了3-4天吧,不多bb,开始步入正题。介绍算法之前,还是像往常一样先介绍一些相关概念。
如果你正在学操作系统,学过死锁,可以直接跳过导入部分,没有学过,那不妨看看导入。
整篇博客大概要花费10-15分钟阅读。
死锁的课本定义是:如果一组进程中的每一个进程都在等待仅由该组进程中的其他进程才能引发的事件,那么该组进程是死锁的。
图片评论:假设线程A持有资源2,线程B持有资源1,他们同时都想申请对方的资源,所有这两个线程就会互相等待而进入死锁状态。
具体知识详见:什么是线程死锁
产生死锁的必要条件:
1.互斥条件: 进程对所分配到的资源进行排他性使用,即在一段时间内,某资源只能被一个进程占用。如果此时还有其他进程请求该资源,则请求进程只能等待,知道占有该资源的进程用毕释放。
2.请求和保持条件: 进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他进程占用,此时请求进程被阻塞,但对自己获得的资源保持不放。
3.不可抢占条件: 进程已获得的资源在未使用完之前不能被抢占,只能在进程使用完时由自己释放。
4.循环等待条件: 在发生死锁时,必然存在一个进程—资源的循环链,即进程集合{p0,p1,p2,···,pn}中的p0正在等待一个p1占用的资源,p1正在等待p2占用的资源,···,pn正在等待已被p0占用的资源。
目前处理死锁的方法可归结为四种:
1.预防死锁: 设置某些限制,破坏产生死锁的四个必要条件中的某个或某些来预防产生死锁。
2.避免死锁: 在资源的动态分配中,用某种方法防止系统进入不安全状态。
3.检测死锁: 事先不采取限制措施,但通过检测机构检测出死锁的发生,然后采取适当的措施,把进程从死锁安全状态中解脱出来。
4.解除死锁: 发生死锁时,采取措施,将进程从死锁状态中解脱出来。常用的方法是撤销一些进程,回收它们的资源,将它们分配的已处于阻塞状态的进程,使其能继续进行。
上述的四种方法,从1-4对死锁的防范程度逐渐减弱,但对应的是资源的利用率的提高,以及进程因资源因素而阻塞的频度下降(即并发程度提高)。
所谓安全状态,是指系统能按某种进程推进顺序(P1,P2,···,Pn)为每个进程Pi分配其所需要的资源,直至满足每个进程对资源的最大需求,使每一个进程都可顺利地完成。此时称(P1,P2,···,Pn)为安全序列。
Ok,我又说了些废话,不知道你听了多少,多少是左耳进又右耳出的。O(∩_∩)O哈哈~不过没多大关系,对于繁杂的知识,能记住就记住,记不住也没什么大不了的。
前面介绍了死锁,那是操作系统中不希望发生的事情,那么如何避免死锁呢?最有代表性的避免死锁的算法是Dijkstra的银行家算法,没错,又是这个人,我还记得他的最短路径算法。。。
但没办法,谁叫他有主角光环呢?
为了实现银行家算法,在系统中必须设置相关的数据结构。
(1)可利用资源向量Available。这是一个含有m个元素的数组,其中的每一个元素代表一类可利用的资源数目,其初始值是系统中所配置的该类全部可用资源的数目,其数值随该类资源的分配和回收而动态地改变。如果 Available[j] = k ,则表示系统中现有 Rj 类资源 K 个。在java中,咱用私有的整形数组表示。
(2)最大需求矩阵Max。这是一个n x m的矩阵,它定义了系统中n个进程中的每一个进程对m类资源的最大需求。如果Max[i,j] = K,则表示进程 i 需要 Rj 类资源的最大数目为 K 个。
(3)分配矩阵Allocation 这也是一个n x m 的矩阵,它定义了系统中每一类资源当前已分配给每一个进程的资源数。如果Allocation[i,j] = K,则表示进程 i 当前已分配的Rj类资源的数目为K。
(4)需求矩阵Need。这也是一个n x m 的矩阵,用以表示每一个进程尚且需要的各类资源数,如果Need[i,j] = K,则表示进程i 还需要Rj类资源K个方能完成其任务。
上述三个矩阵间存在下述关系:
Need[i, j] = Max[i, j] - Allocation[i, j]
上面的陈述都是书上的原话,但是在java中实现又是另外一回事,理解表示数据的方式很基础,书上说要用矩阵,但是用二维数组也能成,那你是选择用矩阵还是二维数组呢,(其实用二维数组也能实现矩阵的相关运算,如加,减,×,转置等),有兴趣的同学可以自己写一个,我就不舍近求远了。
直接放流程图了,怕你们看文字看吐了(hhh),这个算法主要是用来对系统中请求资源。
其中设Request 为二维数组,Request[i] 是进程Pi的请求向量,如果Request[i, j] = K ,则表示进程Pi需要 K 个 Rj 类型的资源。
为了简单明了,还是直接放图,方便大家理解。
其中,工作向量 work = Available, 代表临时系统的可利用资源。Finish代表当前进程是否已经运行完毕。
这就是安全性算法的检验过程。
那么有了这些书本上的主要介绍后,如何写出自己想要的代码并跑起来呢?
思路就是这么个思路,如何迈出自己的第一步,看自己敲代码的熟练程度了。如果不知道如何下手,那么跟着我来盲目分析分析。
先写一个表示进程信息的类吧,目的是为了实现相关的数据结构。我们把它命名为Pcb.java吧。
直接给代码,在介绍一些要注意的事项。
/**
* @author GoldenRetriever
* @date 2019/11/20
*/
class Pcb {
/**
* 系统中进程序列
*/
private String[] pcbOrder;
/**
* 系统中资源序列
*/
private String[] sourceOrder;
/**
* 可利用资源, 如果available[j] = k, 代表系统现有j类资源数目为k个
*/
private int [] available;
/**
* 最大需求资源,定义系统中n个进程中每一个进程对m资源的最大需求,如果max[i][j] = k,
* 则表示进程i需要j类资源的最大数目为 k个。
*/
private int [][] max;
/**
* 已分配资源,定义系统中每一类资源当前已分配给每一进程的资源数。如果allocation[i][j] = k,
* 则表示进程i当前已分得j类资源的数目为 k。
*/
private int [][] allocation;
/**
* 进程需要资源,表示每一个进程尚需的各类资源数,如果 need[i][j] = k,
* 表示进程i还需要j类资源k个方能完成任务。
*/
private int [][] need;
/**
* 资源分配给进程后,判断是否完成
*/
private boolean[] finish;
/**
* 设置相应属性的get和set方法。
*/
String[] getPcbOrder() {
return pcbOrder;
}
void setPcbOrder(String[] strings) {
this.pcbOrder = strings;
}
String[] getSourceOrder() {
return sourceOrder;
}
void setSourceOrder(String[] strings) {
this.sourceOrder = strings;
}
int[] getAvailable() {
return available;
}
void setAvailable(int[] a) {
this.available = a;
}
int[][] getMax() {
return max;
}
void setMax(int[][] a) {
this.max = a;
}
int[][] getAllocation() {
return allocation;
}
void setAllocation(int[][] a) {
this.allocation = a;
}
int[][] getNeed() {
return need;
}
void setNeed(int[][] a) {
this.need = a;
}
boolean[] getFinish() {
return finish;
}
void setFinish(boolean[] a) {
this.finish = a;
}
}
相关解释:
1. 初始化时,我们要输入系统中进程的数目,如{P0, P1, P2, P3, P4}这样五个进程,那么我们要保存下来,以及输入的资源种类,如{A, B, C}这样三类资源。所以我们在设计 Pcb.java 时,增加了 PcbOrder 以及 SourceOrder 两个一维字符串型数组。还设置了一个布尔型数组 Finish ,用来保存资源分配过程中进程的完成状态。
2. 设置相应的get和set方法。封装私有属性,养成好习惯。
好了,设计好相关的属性后,可以开始设计方法了。需要哪些方法,我们再来分析分析。
1.init()初始化方法: 开始的时候要输入进程数目和资源种类相关信息,那么初始化相关信息就是很有必要的了,为什么要独立出来,不直接在main()中直接写呢,当初始化东西代码过多,就可以考虑定义为一个方法了。2017年阿里发布的 阿里编码规约 插件中,对 java 中main()函数的代码长度做出了规范,不超过 80 行,我觉得挺好的,因为有时候,代码全写在main()主函数中并不是一件好事,简短的代码可能感觉不到,但代码一长,有时候就可能导致逻辑混乱。好像又扯远了,但是我们知道这个要写就行了。
2.max()最大需求 : 因为每一个进程都有一个最大需求类,表示当资源分配到最大时,代表进程可以运行完成。
3.hasDistributionSource()已分配: 按照书本的要求,每一个进程在T0时刻都会有一个已经分配好的资源数目,或为0,或为已知的其他量。
4.bank()银行家算法:在算法分析中,我们要求需要设计一个银行家算法,在进程请求资源的时候,判断是否合理。
5.safe() 安全性算法:判断系统是否处于安全状态,不管是开头,还是在分配资源之后,都需要对这个分配之后的资源进行检测。
解释:
1. 有些人可能会说,为什么不把max() 和 hasDistributionSource() 方法放在 init() 中,其实放在一起也没有错,那为什么不放一起呢,我只是想让一个函数或方法,只干一件事 !
2. 这就是全部吗? 并不是,我只是挑出了重点来讲,一些小细节的东西没有必要在这里大声宣扬。留给你们自己发现吧。
3. 其实敲代码的过程中,代码本身并不是最后有规律的模样,只是一步一步规范之后,才有现在的样子,其中还有很多调试时候添加的代码,但那时过程,不是结果,如果你想体验过程,建议自己先敲一遍,只看前面的分析,实在敲不出来后,再看看代码。当然代码也会有不合理的部分,欢迎大佬评论指正。
放代码:说了那么多,知道你们最喜欢的就是下面的。。。
import java.util.Scanner;
import java.util.ArrayList;
/**
* @author GoldenRetriever
* @date 2019/11/20
*/
public class Run {
/**
* 设置一个公有的输入扫描器
*/
private static Scanner scanner = new Scanner(System.in);
/**
* 判断输入是否合法的布尔变量flag
*/
private static boolean flag = false;
/**
* 用来存储安全序列的动态数组
*/
private static ArrayList safeList = new ArrayList();
/**
* 获取用户输入的各种数据的变量
*/
private static String getData;
/**
* 正则字符串,表示多个空格
*/
private static String regex = " +";
/**
* 代表用户输入的进程号
*/
private static int pcbNumber = 0;
/**
* 完成的进程
*/
private static String hasFinishPcb = null;
public static void main(String[] args) {
Pcb pcb = new Pcb();
init(pcb);
max(pcb);
hasDistributionSource(pcb);
printInfo(pcb);
requestPcb(pcb);
}
/**
* 初始化一些基本数据
*/
private static void init(Pcb pcb) {
do {
//获取进程序列
System.out.println("请输入参与算法的进程序列,以空格分开: ");
getData = scanner.nextLine();
String[] orderOfPcb = getData.split(regex);
pcb.setPcbOrder(orderOfPcb);
//获取资源类别序列
System.out.println("请输入参与算法的资源类别序列,以空格分开: ");
getData = scanner.nextLine();
String[] orderOfSources = getData.split(regex);
pcb.setSourceOrder(orderOfSources);
//获取各种资源的数量
System.out.println("请输入各种资源的总数量: ");
getData = scanner.nextLine();
String[] numOfSource = getData.split(regex);
if (numOfSource.length != orderOfSources.length) {
System.out.println("资源种类和资源数量不匹配");
flag = false;
} else {
flag = true;
int[] a = new int[numOfSource.length];
for (int i = 0; i < numOfSource.length; i++) {
a[i] = Integer.parseInt(numOfSource[i]);
}
pcb.setAvailable(a);
}
}while (!flag);
}
/**
* 设置每一进程的最大需求资源
*/
private static void max(Pcb pcb) {
do {
//这个二维数组用来表示每个进程的每类资源的最大需求
int[][] b = new int[pcb.getPcbOrder().length][pcb.getSourceOrder().length];
outer: for (int i = 0; i< pcb.getPcbOrder().length; i++) {
System.out.println("请输入" + pcb.getPcbOrder()[i] + "进程的各类资源最大需求:");
int[] maxNeed = getData();
if (maxNeed.length != pcb.getSourceOrder().length) {
System.out.println("进程最大需求资源数和分配的资源种类不匹配");
flag = false;
break;
} else {
for (int k = 0; k < maxNeed.length; k++) {
if (maxNeed[k] > pcb.getAvailable()[k]) {
System.out.println("该进程对" + pcb.getSourceOrder()[k] + "类资源的最大需求" +
"超过资源的总数量, 无法分配");
flag = false;
break outer;
} else {
flag = true;
//前面的i代表哪类进程,后面的k代表哪类资源
b[i][k] = maxNeed[k];
}
}
}
}
pcb.setMax(b);
}while (!flag);
}
/**
* 设置T0时刻的资源分配情况
*/
private static void hasDistributionSource(Pcb pcb) {
do {
//这个二维数组用来表示每个进程的每类资源的已分配资源数
int[][] c = new int[pcb.getPcbOrder().length][pcb.getSourceOrder().length];
//用来计算分配了资源之后每一进程每个进程的每类资源还需要多少才能完成
int[][] need = new int[pcb.getPcbOrder().length][pcb.getSourceOrder().length];
outer: for (int i = 0; i< pcb.getPcbOrder().length; i++) {
System.out.println("请输入" + pcb.getPcbOrder()[i] + "进程的各类资源已分配资源:");
int[] hasAllocation = getData();
if (hasAllocation.length != pcb.getSourceOrder().length) {
System.out.println("进程最大需求资源数和分配的资源种类不匹配");
flag = false;
break;
} else {
for (int k = 0; k < hasAllocation.length; k++) {
if (hasAllocation[k] > pcb.getAvailable()[k]) {
System.out.println("该进程对" + pcb.getSourceOrder()[k] + "类资源的分配" +
"超过该类资源的总数量, 无法分配");
flag = false;
i = -1;
continue outer;
} else {
flag = true;
//前面的i代表哪类进程,后面的k代表哪类资源
c[i][k] = hasAllocation[k];
need[i][k] = pcb.getMax()[i][k] - hasAllocation[k];
}
}
}
}
pcb.setNeed(need);
int count = 0, sum = 0;
//用来计算分配了资源之后每类资源的剩余数量
int[] afterDistribution = new int[pcb.getAvailable().length];
do {
for (int m = 0; m < pcb.getPcbOrder().length; m++) {
for (int n = count; n < count + 1; n++) {
sum += c[m][n];
}
}
if (sum > pcb.getAvailable()[count]) {
System.out.println("已分配资源不能超过总资源");
break;
} else {
System.out.println(pcb.getSourceOrder()[count] + "类资源分配成功");
pcb.setAllocation(c);
afterDistribution[count] = pcb.getAvailable()[count] - sum;
}
count++;
sum = 0;
}while (count < pcb.getSourceOrder().length);
//所有资源分配成功后再重新计算资源剩余量
pcb.setAvailable(afterDistribution);
}while (!flag);
}
/**
* 因为用户输入最大需求和已分配资源时候,会用相同的方式输入,所以写成一个方法,方便重复使用
* @return 用户输入的资源数组
*/
private static int[] getData() {
getData = scanner.nextLine();
String[] orderOfMax = getData.split(regex);
int[] a = new int[orderOfMax.length];
for (int j = 0; j < orderOfMax.length; j++) {
a[j] = Integer.parseInt(orderOfMax[j]);
}
return a;
}
/**
* 打印进程相关信息
*/
private static void printInfo(Pcb pcb) {
System.out.println("***************");
for (int i = 0;i<pcb.getPcbOrder().length; i++) {
System.out.println("Pcb " + pcb.getPcbOrder()[i] + "最大需求:");
for (int j = 0; j<pcb.getSourceOrder().length; j++) {
System.out.print(pcb.getSourceOrder()[j] + ":" + pcb.getMax()[i][j] + " ");
}
System.out.println();
}
System.out.println("***************");
for (int i = 0;i<pcb.getPcbOrder().length; i++) {
System.out.println("Pcb " + pcb.getPcbOrder()[i] + "已分配: ");
for (int j = 0; j<pcb.getSourceOrder().length; j++) {
System.out.print(pcb.getSourceOrder()[j] + ":" + pcb.getAllocation()[i][j] + " ");
}
System.out.println();
}
System.out.println("***************");
for (int i = 0;i<pcb.getPcbOrder().length; i++) {
System.out.println("Pcb " + pcb.getPcbOrder()[i] + "还需要:");
for (int j = 0; j<pcb.getSourceOrder().length; j++) {
System.out.print(pcb.getSourceOrder()[j] + ":" + pcb.getNeed()[i][j] + " ");
}
System.out.println();
}
System.out.println("***************");
System.out.println("现在可利用资源为: ");
for (int i= 0; i < pcb.getAvailable().length; i++) {
System.out.print(pcb.getSourceOrder()[i] +":" + pcb.getAvailable()[i] + " ");
}
System.out.println();
}
private static void requestPcb(Pcb pcb) {
if (isSafe(pcb)) {
do {
System.out.println("请输入请求资源的进程:");
getData = scanner.nextLine();
for (int i = 0; i< pcb.getPcbOrder().length; i++) {
if (pcb.getPcbOrder()[i].equals(getData)) {
pcbNumber = i;
}
}
System.out.println("你现在正在对" + pcb.getPcbOrder()[pcbNumber] + "分配资源");
System.out.println("请输入该进程分配的资源序列:");
int[] a = getData();
bank(pcb, a, pcbNumber);
System.out.println("是否重新请求资源:(y/Y)");
String s = scanner.nextLine();
String reg = "y", reg1 = "Y";
flag = s.equals(reg) || s.equals(reg1);
}while (flag);
} else {
System.out.println("To时刻的系统没偶遇安全序列。无法分配");
}
}
/**
* 安全性算法 在T0时刻分配好初始资源后,需要利用安全性算法来找到一个安全序列,如果找到,则认为系统是安全的,
* 资源可以继续分配下去,如果没有找到,则认为系统处于不安全状态。
*/
@SuppressWarnings("unchecked")
private static boolean isSafe(Pcb pcb) {
System.out.println("*******安全性算法检验*******");
boolean flag;
boolean[] finish = new boolean[pcb.getPcbOrder().length];
for (int i = 0; i < pcb.getPcbOrder().length; i++) {
finish[i] = false;
}
pcb.setFinish(finish);
for (int i = 0; i < pcb.getPcbOrder().length; i++) {
//i代表哪个进程
int finishNum = 0;
//工作数组,用来暂时计算
int[] work = pcb.getAvailable();
for (int j = 0; j < pcb.getSourceOrder().length; j++) {
//j代表哪类资源
//系统有足够的资源分配给进程,使之运行完成。但分配资源时,例如资源有三类:a,b,c
//那么这个进程所需要的a,b,c资源都要分配到才算完成
if (!pcb.getFinish()[i] && pcb.getNeed()[i][j] <= work[j]) {
finishNum++;
}
}
if (finishNum == pcb.getSourceOrder().length) {
//每类资源都可以分配,状态设置为true,且加入安全序列
for (int j = 0; j < pcb.getSourceOrder().length; j++) {
work[j] = pcb.getAvailable()[j] + pcb.getAllocation()[i][j];
}
pcb.getFinish()[i] = true;
System.out.println("进程" + pcb.getPcbOrder()[i] + "全部资源都分配成功");
safeList.add(pcb.getPcbOrder()[i]);
pcb.setAvailable(work);
//一旦有一个进程分配成功后,要重新遍历所有进程,因为可分配资源增加了。
//这里i要重新置为0,每次都要重新从头开始遍历, 为什么要减1,因为continue之后
//i++了
i = -1;
} else if (pcb.getFinish()[i]) {
System.out.println("进程" + pcb.getPcbOrder()[i] + "已完成");
} else {
System.out.println("进程" + pcb.getPcbOrder()[i] + "现在无法分配");
}
}
int[][] tempPcb = pcb.getAllocation();
int [] tempAvailable = new int[pcb.getAvailable().length];
int count = 0, sum = 0;
int trueNum = 0;
for (int i = 0; i < pcb.getFinish().length; i++) {
if (pcb.getFinish()[i]) {
//如果有进程已完成,为true, 那么+1
trueNum++;
}
}
if (trueNum == pcb.getFinish().length ) {
//判断系统安全后,可利用资源要重新置回原来的值
do {
for (int m = 0; m < pcb.getPcbOrder().length; m++) {
for (int n = count; n < count + 1; n++) {
sum += tempPcb[m][n];
}
}
for (int i = count; i < count +1; i++) {
tempAvailable[i] = pcb.getAvailable()[i] - sum;
}
count++;
sum = 0;
}while (count < pcb.getSourceOrder().length);
pcb.setAvailable(tempAvailable);
//所有的进程都可以分配资源,finish状态都为true,
flag = true;
System.out.println("系统安全,安全序列为: ");
safeList.remove(hasFinishPcb);
for (Object o : safeList) {
System.out.print(o + " ");
}
System.out.println();
//删除数组中的所有元素,但并不是消除数组,而是把数组中的元素置为NULL,大小置为0
safeList.clear();
} else {
flag = false;
}
return flag;
}
/**
* 银行家算法,进程请求资源,然后判断是否可分配,系统先试探着分配给进程,之后执行安全新算法,
* 检查此次资源分配是否使资源处于安全状态,若安全,则正是将资源分配,否则,将本次的试探分配作废,恢复原来状态,让该进程等待
* @param a 代表请求的进程资源序列
* @param x 代表请求资源的是那个进程
*/
private static void bank(Pcb pcb, int[] a, int x) {
System.out.println("*****银行家算法监测*********");
System.out.println("现在可利用资源为: ");
for (int i= 0; i < pcb.getAvailable().length; i++) {
System.out.print(pcb.getSourceOrder()[i] +":" + pcb.getAvailable()[i] + " ");
}
System.out.println();
//进程pi的请求资源变量,如果request[i][j] = k ,则表示进程pi需要K个j类型的资源。
int[][] request = new int[pcb.getPcbOrder().length][pcb.getSourceOrder().length];
//用来存储试探分配的资源
int[] availableTemp = new int[pcb.getAvailable().length];
int[][] allocationTemp = new int[pcb.getPcbOrder().length][pcb.getSourceOrder().length];
int[][] needTemp = new int[pcb.getPcbOrder().length][pcb.getSourceOrder().length];
//初始化allocation和need
for (int m = 0; m < pcb.getPcbOrder().length; m++) {
for (int n = 0; n < pcb.getAvailable().length; n++) {
allocationTemp[m][n] = pcb.getAllocation()[m][n];
needTemp[m][n] = pcb.getNeed()[m][n];
}
}
for (int i = 0; i < pcb.getPcbOrder().length; i++) {
if (i == x) {
if (a.length != pcb.getSourceOrder().length) {
System.out.println("进程请求的资源数和已有的资源种类不匹配");
flag = false;
} else {
int numOfAll = 0;
System.out.println("该" + pcb.getPcbOrder()[i] + "进程所需要的资源为");
for (int j = 0; j<pcb.getSourceOrder().length; j++) {
System.out.print(pcb.getNeed()[i][j] + " ");
}
System.out.println();
for (int k = 0; k < a.length; k++) {
//是否超过最大需求量
if (a[k] > pcb.getNeed()[i][k]) {
System.out.println("该进程对" + pcb.getSourceOrder()[k] + "类资源的请求超过" +
"超过他所需要的最大值, 无法分配");
flag = false;
} else if (a[k] > pcb.getAvailable()[k]) {
System.out.println("尚无足够资源分配" + pcb.getPcbOrder()[i] + "进程需等待!");
flag = false;
}else if ( a[k] <= pcb.getNeed()[i][k] && a[k] <= pcb.getAvailable()[k]) {
numOfAll++;
}
}
//所有资源都可分配
if (numOfAll == pcb.getSourceOrder().length) {
for (int k = 0; k < pcb.getSourceOrder().length; k++) {
request[i][k] = a[k];
//试探分配,修改这个进程的相关信息
availableTemp[k] = pcb.getAvailable()[k] -request[i][k];
allocationTemp[i][k] = pcb.getAllocation()[i][k] + request[i][k];
needTemp[i][k] = pcb.getNeed()[i][k] - request[i][k];
}
//某个进程的所有资源都试探分配完成后,先假设为正确的分配出去
pcb.setAvailable(availableTemp);
pcb.setAllocation(allocationTemp);
pcb.setNeed(needTemp);
if (isSafe(pcb)) {
int isMaxEqAllocation = 0;
System.out.println("系统处于安全状态,分配成功");
for (int m = 0; m < pcb.getAvailable().length; m++) {
if (pcb.getMax()[i][m] == pcb.getAllocation()[i][m]) {
isMaxEqAllocation++;
}
}
if (isMaxEqAllocation == pcb.getSourceOrder().length) {
//如果每一列最大需求等于分配量,那么说明该进程完成,应该归还分配的可用资源
for (int m = 0; m < pcb.getAvailable().length; m++) {
availableTemp[m] = pcb.getAvailable()[m] + pcb.getAllocation()[i][m];
}
System.out.println(pcb.getPcbOrder()[i] + "资源分配等于最大需求,运行完成");
hasFinishPcb = pcb.getPcbOrder()[i];
pcb.setAvailable(availableTemp);
}
System.out.println("分配成功后,可利用资源为: ");
for (int m= 0; m < pcb.getAvailable().length; m++) {
System.out.print(pcb.getSourceOrder()[m] +":" + pcb.getAvailable()[m] + " ");
}
System.out.println();
} else {
//处于不安全状态,反过来恢复值
System.out.println("系统处于不安全状态,收回分配的资源");
for (int k = 0; k < pcb.getSourceOrder().length; k++) {
availableTemp[k] = pcb.getAvailable()[k] + request[i][k];
allocationTemp[i][k] = pcb.getAllocation()[i][k] - request[i][k];
needTemp[i][k] = pcb.getNeed()[i][k] + request[i][k];
}
System.out.println("分配失败,可利用资源为: ");
for (int m= 0; m < pcb.getAvailable().length; m++) {
System.out.print(pcb.getSourceOrder()[m] +":" + pcb.getAvailable()[m] + " ");
}
System.out.println();
pcb.setAllocation(allocationTemp);
pcb.setNeed(needTemp);
}
}
}
}
}
}
}
已书上给出的例子来分析过程吧。
假设系统中有五个进程{P0,P1,P2,P3,P4}和三类资源{A,B,C},各种资源的数量分别为10,5, 7。在T0时刻资源的分配情况如下表:
Max | Allocation | Need | Available | |||||||||
A | B | C | A | B | C | A | B | C | A | B | C | |
P0 | 7 | 5 | 3 | 0 | 1 | 0 | 7 | 4 | 3 | 3 | 3 | 2 |
P1 | 3 | 2 | 2 | 2 | 0 | 0 | 1 | 2 | 2 | |||
P2 | 9 | 0 | 2 | 3 | 0 | 2 | 6 | 0 | 0 | |||
P3 | 2 | 2 | 2 | 2 | 1 | 1 | 0 | 1 | 1 | |||
P4 | 4 | 3 | 3 | 0 | 0 | 2 | 4 | 3 | 1 |
(1) 在T0时刻的安全性:利用安全性算法进行检测,发现存在一个安全序列{P1, P3, P4, P2,P0},即系统只要按照这个序列分配P1资源,然后运行完的进程收回其分配的资源,再依次重复进行,直到所有进程都执行完毕。其实T0时刻的安全性算有12 = 2 * 6 种,只要开始是(P1,P3)或者(P3, P1)那么对于后面的三个进程,随便哪个开始都能满足要求。但是只要找到一个就能说明系统处于安全状态。
比如代码测试过程中,找到的安全序列为:{P1, P3, P0, P2, P4}。
(2)现在假设P1开始请求资源:(1, 0 ,2),那么我们应该知道,知道它请求的资源<=(1,2,2),那么就可以分配,因为分配了1,0,2之后,安全序列还是原来的安全序列。
(3)在(2)的基础上,分配成功后,可利用资源从(3,3,2)变成(2,3,0)。现在假设P4突然请求资源(3,3,0)。我们应该一眼就知道,A类资源可用为2,但现在你要申请3,所以肯定不合理,进程不应该被执行。
(4)此时P4不被执行,那再让P0请求一个(0,2,0)吧,可利用资源还是(2,3,0)按理来说可以申请,但是我们试着分配一下。
Allocation | Need | Available | |||||||
A | B | C | A | B | C | A | B | C | |
P0 | 0 | 3 | 0 | 7 | 2 | 3 | 2 | 1 | 0 |
P1 | 3 | 0 | 2 | 0 | 2 | 0 | |||
P2 | 3 | 0 | 2 | 6 | 0 | 0 | |||
P3 | 2 | 1 | 1 | 0 | 1 | 1 | |||
P4 | 0 | 0 | 2 | 4 | 3 | 1 |
但是用安全性算法进行检测时,却并没有对它进行分配。
为什么是这样的,观看表格中假设分配的资源可以看出,剩下的(2,1, 0)已经无法对任何一个进程进行分配了,因为即使分配了也无法满足任何进程的需要,此时系统处于不安全专题,那么只能收回分配的资源了。
看到这里,你应该能理解并掌握银行家算法了,但是代码中具体的实现我还是希望你能够去仔细品味,看看到底是怎么实现的,比如isSafe()方法中的多重循环,bank()方法中如果分配了资源,但是系统不安全,要收回资源又是如何收回的。其实真正难的就是那两个方法isSafe() 和 bank(),如果你真的想搞明白,或许你可以dang下代码,自己去运行一下,实践出真知。废话不多说,希望你没有白费时间看这篇博客,如果有什么不懂的,欢迎评论;如果文章有什么不妥之处,也欢迎您指出,这期算法就到这了,下次再见!