時間片輪轉(Round-Robin)排程演算法是作業系統一種比較公平的程式排程的方式,這種方式使得就緒佇列上的所有程式在每次輪轉時都可以執行相同的一個時間片。
基本原理
演算法實現原理是,按程式到達順序(FCFS 原則)將程式依次加入就緒佇列當中,然後將 CPU 分配給位於隊首的程式,確定一個時間片,讓該程式執行一個時間片。當該程式執行時間到時,該程式可能已經執行完畢(可能在時間片未到時就以及執行完畢),或者未執行完畢,如果是前者只需將程式彈出佇列即可,如果是後者則將該程式加入隊尾,並將 CPU 分配給新的隊首程式,如此迴圈。
程式切換時機
程式在執行時分為兩種情況
- 在該時間片內程式執行完畢,這種情況排程程式將立即把該程式彈出佇列,並把 CPU 分配給新的隊首程式
- 在該時間片內程式未執行完畢,排程程式將立即中斷該程式執行,把該程式加入隊尾,並將 CPU 分配給新的隊首程式
時間片大小的確定
在 RR 演算法中,時間片的大小直接影響了系統的效能。
- 時間片過小,有利於短作業,但是會頻繁地切換程式,增加了系統的開銷,影響效能。
- 時間片過大,演算法退化成 FCFS 演算法,如果某個短作業程式之前的程式都是長作業,將導致後面的短作業程式長時間等待。
有關的計算
- 週轉時間 = 程式完成時間 - 程式到達時間
- 帶權週轉時間 = 程式週轉時間 / 程式實際執行時間
- 平均週轉時間 = (程式1週轉時間 + ... + 程式n週轉時間)/ n
- 平均帶權週轉時間 = (程式1帶權週轉時間 + ... + 程式n帶權週轉時間)/ n
實現
package cn.vecrates.operatingSystem;
import java.util.*;
import java.util.concurrent.LinkedBlockingQueue;
public class RR {
private int mProcessCount; //程式數
private Queue<Process> mReadyQueue; //就緒佇列,存放“待執行的程式
private Queue<Process> mUnreachQueue; //存放“到達時間未到的程式”
private int mTimeSlice; //時間片
private Queue<Process> mExecutedQueue; //執行完畢的程式佇列
private double mTotalWholeTime = 0.0;
private double mTotalWeightWholeTime = 0.0;
private RR(int processCount, Queue<Process> processQueue, int timeSlice) {
this.mProcessCount = processCount;
this.mUnreachQueue = processQueue;
mReadyQueue = new LinkedBlockingQueue<>();
this.mTimeSlice = timeSlice;
mExecutedQueue = new LinkedList<>();
}
/**
* RR 演算法實現
*/
public void RRAlgorithm() {
mReadyQueue.add(mUnreachQueue.poll());
Process currProcess = mReadyQueue.poll();
//第一個程式執行
int currTime = executeProcess(currProcess, 0);
while(!mReadyQueue.isEmpty() || !mUnreachQueue.isEmpty()) {
//把所有“到達時間”達到的程式加入執行佇列頭部
while(!mUnreachQueue.isEmpty()) {
if(mUnreachQueue.peek().getArrivalTime() <= currTime) {
mReadyQueue.add(mUnreachQueue.poll());
} else {
break;
}
}
if(currProcess.getRemainServiceTime() > 0) mReadyQueue.add(currProcess);
//執行佇列不為空時
if(!mReadyQueue.isEmpty()) {
currProcess = mReadyQueue.poll();
currTime = executeProcess(currProcess, currTime);
} else {
//當前沒有程式執行,但還有程式為到達,時間直接跳轉到到達時間
currTime = mUnreachQueue.peek().getArrivalTime();
}
}
}
private int executeProcess(Process currProcess, int currTime) {
if(currProcess.getRemainServiceTime() - mTimeSlice <= 0) {
//當前程式在這個時間片內能執行完畢
showExecuteMessage(currTime, currTime += currProcess.getRemainServiceTime(), currProcess.getName());
currProcess.setFinishTime(currTime);
currProcess.setRemainServiceTime(0);
//對週轉時間等進行計算
calculeteWholeTime(currProcess);
calculateWeightWholeTime(currProcess);
mTotalWholeTime += currProcess.getWholeTime();
mTotalWeightWholeTime += currProcess.getWeightWholeTime();
mExecutedQueue.add(currProcess);
} else {
//不能執行完畢
showExecuteMessage(currTime, currTime += mTimeSlice, currProcess.getName());
currProcess.setRemainServiceTime(currProcess.getRemainServiceTime() - mTimeSlice);
}
return currTime;
}
/**
* 計算週轉時間
* @param process
*/
private void calculeteWholeTime(Process process) {
process.setWholeTime(process.getFinishTime() - process.getArrivalTime());
}
/**
* 計算帶權週轉時間
* @param process
*/
private void calculateWeightWholeTime(Process process) {
process.setWeightWholeTime(process.getWholeTime() / (double)process.getServiceTime());
}
private void showExecuteMessage(int startTime, int endTime, String name) {
System.out.println(startTime + "~" + endTime + ":【程式" + name + "】執行");
}
public void showResult() {
System.out.print("程式名\t");
System.out.print("週轉時間\t");
System.out.println("帶權週轉時間\t");
Process process ;
while(!mExecutedQueue.isEmpty()) {
process = mExecutedQueue.poll();
System.out.print("程式" + process.getName() + "\t");
System.out.print("\t" + process.getWholeTime() + "\t");
System.out.println("\t" + process.getWeightWholeTime() + "\t");
}
System.out.println("平均週轉時間:" + mTotalWholeTime / (double) mProcessCount);
System.out.println("平均帶權週轉時間:" + mTotalWeightWholeTime / (double) mProcessCount);
}
public static void printAll(Queue<Process> queue) {
System.out.print("程式名\t");
System.out.print("到達時間\t");
System.out.println("服務時間\t");
Process process = null;
while (!queue.isEmpty()){
process = queue.poll();
System.out.print("程式" + process.getName() + "\t");
System.out.print("\t" + process.getArrivalTime() + "\t");
System.out.println("\t" + process.getServiceTime() + "\t");
}
}
public static void main(String[] args) throws InterruptedException {
Scanner scanner = new Scanner(System.in);
System.out.println("輸入程式個數:");
int processCount = scanner.nextInt();
if(processCount < 1) return;
Queue<Process> queue = new LinkedBlockingQueue<>();
System.out.println("依次輸入每個程式的到達時間(按到達順序):");
for(int i=0; i<processCount; i++) {
Process process = new Process((char)(i + 65) + "");
process.setArrivalTime(scanner.nextInt());
queue.add(process);
}
System.out.println("依次輸入每個程式的服務時間(按到達順序):");
for(int i=0; i<processCount; i++) {
Process process = queue.poll();
int time = scanner.nextInt();
process.setServiceTime(time);
process.setRemainServiceTime(time);
queue.add(process);
}
System.out.println("輸入時間片大小");
int timeSlice = scanner.nextInt();
RR rr = new RR(processCount, queue, timeSlice);
System.err.println("*******************程式情況*****************");
Thread.sleep(1000);
printAll(new LinkedBlockingQueue<>(queue));
Thread.sleep(1000);
System.err.println("******************執行過程*****************");
Thread.sleep(1000);
rr.RRAlgorithm();
Thread.sleep(1000);
System.err.println("*******************執行結果*****************");
Thread.sleep(1000);
rr.showResult();
}
}
//程式
class Process {
private String name;
private int arrivalTime; //到達時間
private int serviceTime; //服務時間
private int remainServiceTime; //還需要服務的時間
private int finishTime; //完成時間
private int wholeTime; //週轉時間
private double weightWholeTime; //帶權週轉時間
public int getRemainServiceTime() {
return remainServiceTime;
}
public void setRemainServiceTime(int remainServiceTime) {
this.remainServiceTime = remainServiceTime;
}
public Process(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getArrivalTime() {
return arrivalTime;
}
public void setArrivalTime(int arrivalTime) {
this.arrivalTime = arrivalTime;
}
public int getServiceTime() {
return serviceTime;
}
public void setServiceTime(int serviceTime) {
this.serviceTime = serviceTime;
}
public int getFinishTime() {
return finishTime;
}
public void setFinishTime(int finishTime) {
this.finishTime = finishTime;
}
public int getWholeTime() {
return wholeTime;
}
public void setWholeTime(int wholeTime) {
this.wholeTime = wholeTime;
}
public double getWeightWholeTime() {
return weightWholeTime;
}
public void setWeightWholeTime(double weightWholeTime) {
this.weightWholeTime = weightWholeTime;
}
}
複製程式碼
執行結果 當時間片為 1 時:
時間片為 4 時: