Java语言实现SJF算法思想_Java模拟操作系统实验一:四种进程调度算法实现(FCFS,SJF,RR,HRN)...

本文介绍了如何使用Java实现操作系统中的经典调度算法,包括先来先服务(FCFS)、短作业优先(SJF)、时间片轮转(RR)和最高响应比优先(HRN)。通过使用队列数据结构和自定义比较器,作者详细解释了算法的思路和代码实现,并提供了实验结果和分析。代码经过优化,降低了不同算法之间的耦合性,适用于模拟进程调度的场景。
摘要由CSDN通过智能技术生成

前言

刚学完操作系统,模拟实现了其中一些经典的算法,内容比较多,打算写一个系列的总结,将自己的源码都分享出来,既方便自己以后复习,也希望能帮助到一些刚入坑的小伙伴。我的所有代码的运行环境都是基于Eclipse,jdk1.10下。

1.问题概述

编程实现常用调度算法,即先来先服务、短作业(进程)优先、时间片轮转以及最高响应比优先调度算法。编程语言及环境不限。须给出关键数据结构、算法以及变量的详细说明与注释。

2.算法简介

1、时间片轮转调度算法(RR):给每个进程固定的执行时间,根据进程到达的先后顺序让进程在单位时间片内执行,执行完成后便调度下一个进程执行,时间片轮转调度不考虑进程等待时间和执行时间,属于抢占式调度。优点是兼顾长短作业;缺点是平均等待时间较长,上下文切换较费时。适用于分时系统。

2、先来先服务调度算法(FCFS):根据进程到达的先后顺序执行进程,不考虑等待时间和执行时间,会产生饥饿现象。属于非抢占式调度,优点是公平,实现简单;缺点是不利于短作业。

3、高响应比优先调度算法(HRN):根据“响应比=(进程执行时间+进程等待时间)/ 进程执行时间”这个公式得到的响应比来进行调度。高响应比优先算法在等待时间相同的情况下,作业执行的时间越短,响应比越高,满足段任务优先,同时响应比会随着等待时间增加而变大,优先级会提高,能够避免饥饿现象。优点是兼顾长短作业,缺点是计算响应比开销大,适用于批处理系统。

4.短进程优先算法(SJF):以作业的长短来计算优先级,作业越短,其优先级越高。作业的长短是以作业所要求的运行时间来衡量的。

3.思路分析

一开始真的无从下手,想了半天就实现了个FCFS(还只是单纯的把容器内的作业按到达时间排个序号,其实想法是错误的,因为我无法预知到达的次序)。后来注意到了一个贯穿始终的东西——队列。顺着这条思路,通过一点一点深入的分析,一切仿佛豁然开朗,主体的框架写出后,后面就水到渠成了。

首先是确定三个容器,第一个存放所有进程的信息(只是拿来遍历),第二个为队列进行主要的操作(各种算法下的进出队列操作),第三个用来保存已经处理过后的进程(输出结果,便于观察)。所有的核心都集中在了如何进出队列,更进一步的是如何在不同的规则下排序,所以只要定义不同算法的排序比较器,按这个来进出队列就成了。之后一些细节工作,比如分清是全局或局部变量,代码的先后执行次序,如果有可复用的代码就写成一个方法来调用等等,通过不断的调试修改一点点完善。

4.代码实现

package Process;

public class JCB {

String name;//进程名

int arriveTime;//到达时间

int serveTime;//服务时间

int beginTime;//开始时间

int finshTime;//结束时间

int roundTime;//周转时间

double aveRoundTime;//带权周转时间

double clock=0;//在时间轮转调度算法中,记录该进程真实服务时间已经用时的时长

int waitTime;//记录每个进程到达后的等待时间,只用于最高响应比优先调度算法中

public JCB() {

}

public JCB(String name, int arriveTime, int serveTime,double priority) {

super();

this.name = name;

this.arriveTime = arriveTime;

this.serveTime = serveTime;

this.waitTime=0;

}

public String toString() {

String info=new String("进程名:P"+this.name);

return info;

}

}

package Process;

import java.util.ArrayList;

import java.util.Collections;

import java.util.Comparator;

import java.util.LinkedList;

public class processMenu {

ArrayList jcb;// 存放所有进程

LinkedList link;// 存放已经进入队列的进程

ArrayList new_jcb;// 存放按指定调度算法排序后的进程

JCB nowProess;// 当前应执行进程

public void init() {//初始化

jcb = new ArrayList();

link = new LinkedList();

new_jcb = new ArrayList();

JCB p1 = new JCB("1", 0, 4,3);

JCB p2 = new JCB("2", 1, 3,2);

JCB p3 = new JCB("3", 2, 5,3);

JCB p4 = new JCB("4", 3, 2,1);

JCB p5 = new JCB("5", 4, 4,5);

jcb.add(p1);jcb.add(p2);jcb.add(p3);jcb.add(p4);jcb.add(p5);

//先将jcb排序,便于下面的算法实现,就不需要再定义一个标识进程是否已到达的boolean,即无需每次都从头开始扫描jcb容器,

//而是用一个K记录下当前已经扫描到的位置,一次遍历即可,提高了算法效率。

Collections.sort(jcb, new compareAt_St());

}

public void FCFS(){//先来先服务算法

ProcessQueue pq=new ProcessQueue();//调用内部类

pq.Enqueue();//让最先到达的进程先入队

System.out.println("*****************************************************");

while(!link.isEmpty()) {

pq.DisplayQueue();//打印当前队列中的进程

pq.Dequeue();//出队,一次一个

pq.Enqueue();//已到达的进程入队

}

}

public void SJF() {// 短作业优先算法

ProcessQueue pq=new ProcessQueue();

pq.Enqueue();

System.out.println("*****************************************************");

while(!link.isEmpty()) {

pq.DisplayQueue();//打印当前队列中的进程

pq.Dequeue();//出队,一次一个

pq.Enqueue();//已到达的进程入队

Collections.sort(link, new compareSt());//队列中的进程还需按服务时间长度进行排序

}

}

public void RR() {//时间片轮转调度算法

ProcessQueue pq=new ProcessQueue();

pq.Enqueue();

System.out.println("*****************************************************");

while(!link.isEmpty()) {

pq.DisplayQueue();//打印当前队列中的进程

pq.Dequeue(1);//出队,一次一个,因为上一轮出的得让刚到达的进程先进队列,所以没办法,进队操作只能也放在这个函数里了

}

}

public void HRN() {//最高响应比优先调度算法

ProcessQueue pq=new ProcessQueue();

pq.Enqueue();

System.out.println("*****************************************************");

while(!link.isEmpty()) {

pq.DisplayQueue();//打印当前队列中的进程

pq.Dequeue();//出队,一次一个

pq.Enqueue();//已到达的进程入队

Collections.sort(link, new comparePriority());//队列中的进程还需按响应比进行排序

}

}

class ProcessQueue{

int k = 0;// jcb中的进程遍历时的下标

int nowTime = 0;// 当前时间

double sliceTime;//轮转调度时间片

int i=0;//记录当前出入队列的次数

public void Enqueue() {//进程首次入队,可一次进多个

while (k < jcb.size()) {//当遍历完jcb中的所有进程时结束

if (jcb.get(k).arriveTime <= nowTime) {//已经到达的进程按到达时间先后进入队列

link.add(jcb.get(k));

k++;

} else {

break;//如果该进程还未入队,即先结束遍历,保留当前下标k值,注意:此处不要k--;

}

}

}

public void Dequeue() {//进程出队,一次只出一个

nowProess = link.removeFirst();//移除队列的队首元素并且返回该对象元素

nowProess.beginTime = nowTime;//计算开始时间,即为上一个进程的结束时间

nowProess.finshTime = nowProess.beginTime + nowProess.serveTime;//计算结束时间,该进程开始时间+服务时间

nowProess.roundTime = nowProess.finshTime - nowProess.arriveTime;//计算周转时间

nowProess.aveRoundTime = (double) nowProess.roundTime / nowProess.serveTime;//计算平均周转时间

nowTime = nowProess.finshTime;//获得结束时间,即当前时间,方便判断剩下的进程是否已到达

new_jcb.add(nowProess);//经处理过数据后加入new_jcb容器

for(int i=0;i

link.get(i).waitTime++;//所有进入等待队列的进程等待时间+1,此处只为最高响应比算法所用

}

}

public void Dequeue(double sliceTime) {//重载Dequeue方法,实现轮转调度算法的出队

nowProess = link.removeFirst();//移除队列的队首元素并且返回该对象元素

nowTime+=sliceTime;//每次出队,用时一个时间片,更新当前时间

nowProess.clock+=sliceTime;//更新当前出队列的进程已服务时间

if(nowProess.clock>=nowProess.serveTime) {

nowProess.finshTime=nowTime;//计算该进程完成时间

nowProess.roundTime = nowProess.finshTime - nowProess.arriveTime;//计算周转时间

nowProess.aveRoundTime = (double) nowProess.roundTime / nowProess.serveTime;//计算平均周转时间

new_jcb.add(nowProess);//经处理过数据后加入new_jcb容器

}

else {

Enqueue();//已到达的进程先入队

link.addLast(nowProess);//上一轮出的再紧接着进入队尾

}

}

public void DisplayQueue(){//队列中等候的进程

i++;

System.out.println("第"+i+"次队列中排队的进程:"+link);

}

}

public void printProcess() {

System.out.println("进程名 到达时间 服务时间 开始时间 完成时间 周转时间 带权周转时间");

for (int i = 0; i < new_jcb.size(); ++i) {

System.out.println("P"+new_jcb.get(i).name + " " + new_jcb.get(i).arriveTime + " " +

new_jcb.get(i).serveTime+ " " + new_jcb.get(i).beginTime + " " + new_jcb.get(i).finshTime +

" "+ new_jcb.get(i).roundTime + " " + new_jcb.get(i).aveRoundTime);

}

new_jcb.clear();//清空new_jcb容器内的内容,方便存储各种算法的结果并展示

}

}

class compareSt implements Comparator {// 按服务时间升序

public int compare(JCB arg0, JCB arg1) {

return arg0.serveTime - arg1.serveTime;

}

}

class compareAt_St implements Comparator {// 按到达时间升序,若到达时间相同,按服务时间升序

public int compare(JCB o1, JCB o2) {

int a = o1.arriveTime - o2.arriveTime;

if (a > 0)

return 1;

else if (a == 0) {

return o1.serveTime > o2.serveTime ? 1 : -1;

} else

return -1;

}

}

class comparePriority implements Comparator{//按响应比升序排序

public int compare(JCB o1, JCB o2) {

double r1=(double)o1.waitTime/o1.serveTime;

double r2=(double)o2.waitTime/o2.serveTime;

return r1>r2?1:-1;

}

}

5.实验结果与分析

测试数据为(怕有人看不到,就是初始化那段代码):

6636b42a2f2b5675f2eb882e889a0f28.png

调用展示结果:

public class TestProcess {

public static void main(String[] args) {

processMenu pm=new processMenu();

pm.init();//初始化容器

pm.FCFS();pm.printProcess();

pm.SJF();pm.printProcess();

pm.RR();pm.printProcess();

pm.HRN();pm.printProcess();

}

}

5.1 先来先服务算法

f11baa0f68ebd9ac2a5118cb89f21103.png

5.2 短作业优先算法

a72498808ec01edb3b1dfd6b3da069cb.png

5.3 时间片轮转调度算法

0640b5269d730c2b087ae36292f1702d.png

5.4最高响应比优先调度算法

72c4de26787fbdaebea4c6fdbebefa0a.png

因为是一次性实现四个算法,为了降低代码的耦合,我做了一些优化,那个队列就像一个媒介,每个算法都可以用到,为了最大程度的代码复用就抽象成一个类。这里面细节太多,很多都不好描述,队列的进出过程都在上面了,一定要自己动动手把进出队列的图画画,再结合我的注释,帮助理解。

补充:我是用容器来写的,这样能省下很多繁琐的代码,比如我要比较并排列队列内的进程顺序,写个比较器就好了,清晰简洁明了,不要重复造轮子呀,分清主次!

完整代码请参考https://download..net/download/qq_37373250/10888877

=====================手动分割线========补充内容================================

楼下有人提出我写的RR算法有个bug,就是说在时间片为1的时候一切正常,时间片设为4时出错了,我来演示一下

36e4058b00e0a2aa8988146a81daba02.png

结果是这样的:

7cc54388344ecabe70e8f6fdb35204d7.png

很显然错了,那错哪里呢?说来也简单,问题出在这:

ed446239089f2d5e58aaa226e121e5d7.png

此时时间片为4,p1进来后不再执行else里面的语句,因为if(4>=4)为true,执行这语句块内代码,服务时间刚好用完,此时link队列为空(因为没有新进程入队,我的疏漏就在这),然后该方法执行完后回到RR算法内

be6c2edc1a521167c5da4bbce7e33192.png

好了,此时link队列为空,那就没然后了,跳出循环,结束,没下文了!

知道这个要改就容易了,加上这句话,假如进入的进程在时间片内就直接服务完了,就让剩下的进程入队

1f35c13f022795e092fb3eca30d48aaa.png

时间片为4时,结果如下:

d0f2f3e3b6c7bc17c1b1dd981bdd4342.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值