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