Java模擬操作系統實驗一:四種進程調度算法實現(FCFS,SJF,RR,HRN)

前言

剛學完操作系統,模擬實現了其中一些經典的算法,內容比較多,打算寫一個系列的總結,將自己的源碼都分享出來,既方便自己以後複習,也希望能幫助到一些剛入坑的小夥伴。我的所有代碼的運行環境都是基於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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章