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

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_37373250/article/details/85526518

前言

刚学完操作系统,模拟实现了其中一些经典的算法,内容比较多,打算写一个系列的总结,将自己的源码都分享出来,既方便自己以后复习,也希望能帮助到一些刚入坑的小伙伴。我的所有代码的运行环境都是基于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> jcb;// 存放所有进程
	LinkedList<JCB> link;// 存放已经进入队列的进程
	ArrayList<JCB> new_jcb;// 存放按指定调度算法排序后的进程
	JCB nowProess;// 当前应执行进程

	public void init() {//初始化
		jcb = new ArrayList<JCB>();
		link = new LinkedList<JCB>();
		new_jcb = new ArrayList<JCB>();
		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.size();++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<JCB> {// 按服务时间升序
	public int compare(JCB arg0, JCB arg1) {
		return arg0.serveTime - arg1.serveTime;
	}
}

class compareAt_St implements Comparator<JCB> {// 按到达时间升序,若到达时间相同,按服务时间升序
	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<JCB>{//按响应比升序排序

	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.实验结果与分析

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

调用展示结果:

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 先来先服务算法

 

5.2 短作业优先算法

5.3 时间片轮转调度算法

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

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

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

猜你喜欢

转载自blog.csdn.net/qq_37373250/article/details/85526518