返回

Java绘制最短作业优先抢占式算法甘特图

java

使用Java绘制最短作业优先抢占式算法的甘特图

在操作系统课程中,我们经常会遇到各种进程调度算法,其中最短作业优先(SJF)抢占式算法以其高效性而备受关注。为了更直观地理解SJF算法的运作方式,我们可以借助甘特图进行可视化。本文将介绍如何使用Java语言绘制甘特图,清晰地展现SJF抢占式算法的进程调度过程。

进程调度与甘特图

首先,我们需要明确SJF抢占式算法的基本原理:当一个新的进程到达时,如果它的执行时间比当前正在执行的进程短,则会抢占CPU,优先执行。这种调度方式可以最大程度地减少平均等待时间。

甘特图则是一种以图形方式表示进程调度过程的工具。它通常由一个水平时间轴和一系列表示进程执行时间段的矩形组成。通过观察甘特图,我们可以清晰地了解每个进程的开始时间、结束时间以及CPU的占用情况。

Java实现

数据结构

为了表示进程信息,我们可以创建一个名为Process的类,包含以下属性:

  • id:进程ID
  • arrivalTime:到达时间
  • burstTime:执行时间
  • startTime:开始时间
  • completionTime:完成时间
class Process {
    int id;
    double arrivalTime;
    double burstTime;
    double startTime;
    double completionTime;

    public Process(int id, double arrivalTime, double burstTime) {
        this.id = id;
        this.arrivalTime = arrivalTime;
        this.burstTime = burstTime;
    }
}

SJF算法实现

接下来,我们需要实现SJF抢占式算法的核心逻辑。可以使用一个优先队列来维护待执行的进程,并根据执行时间进行排序。当一个新的进程到达时,将其插入队列;当CPU空闲时,从队列中取出执行时间最短的进程执行。

import java.util.PriorityQueue;
import java.util.Comparator;
import java.util.List;
import java.util.ArrayList;

// ... (其他代码)

public List<Process> scheduleSJF(List<Process> processes) {
    PriorityQueue<Process> queue = new PriorityQueue<>(Comparator.comparingDouble(p -> p.burstTime));
    double currentTime = 0;
    int completedProcesses = 0;

    while (completedProcesses < processes.size()) {
        // 将到达的进程添加到队列
        for (Process p : processes) {
            if (p.arrivalTime <= currentTime && p.startTime == 0) {
                queue.add(p);
            }
        }

        // 如果队列为空,则推进时间
        if (queue.isEmpty()) {
            currentTime = processes.stream()
                    .filter(p -> p.arrivalTime > currentTime)
                    .mapToDouble(p -> p.arrivalTime)
                    .min()
                    .orElse(currentTime);
            continue;
        }

        // 取出执行时间最短的进程
        Process currentProcess = queue.poll();
        currentProcess.startTime = currentTime;
        currentTime += currentProcess.burstTime;
        currentProcess.completionTime = currentTime;
        completedProcesses++;

        // 检查是否有进程在当前进程执行期间到达并需要抢占
        for (Process p : processes) {
            if (p.arrivalTime > currentProcess.startTime && p.arrivalTime < currentProcess.completionTime && p.startTime == 0) {
                // 将当前进程剩余时间重新加入队列
                double remainingTime = currentProcess.completionTime - p.arrivalTime;
                currentProcess.burstTime = remainingTime;
                currentProcess.completionTime = 0;
                queue.add(currentProcess);

                // 更新当前时间
                currentTime = p.arrivalTime;
                break;
            }
        }
    }

    return processes;
}

甘特图绘制

最后,我们可以使用Java的图形库(例如Swing或JavaFX)绘制甘特图。以下是一个使用Swing绘制甘特图的示例代码:

// ... (其他代码)

import javax.swing.*;
import java.awt.*;

public class GanttChart extends JPanel {
    private List<Process> processes;

    public GanttChart(List<Process> processes) {
        this.processes = processes;
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);

        int chartWidth = getWidth();
        int chartHeight = getHeight();
        double maxCompletionTime = processes.stream().mapToDouble(p -> p.completionTime).max().orElse(0);

        // 绘制时间轴
        g.drawLine(0, chartHeight / 2, chartWidth, chartHeight / 2);
        for (double i = 0; i <= maxCompletionTime; i++) {
            int x = (int) (i / maxCompletionTime * chartWidth);
            g.drawLine(x, chartHeight / 2 - 5, x, chartHeight / 2 + 5);
            g.drawString(String.format("%.1f", i), x - 10, chartHeight / 2 + 20);
        }

        // 绘制进程矩形
        Random random = new Random();
        for (Process p : processes) {
            int x = (int) (p.startTime / maxCompletionTime * chartWidth);
            int width = (int) (p.burstTime / maxCompletionTime * chartWidth);
            Color color = new Color(random.nextInt(256), random.nextInt(256), random.nextInt(256));
            g.setColor(color);
            g.fillRect(x, chartHeight / 2 - 20, width, 40);
            g.setColor(Color.BLACK);
            g.drawString("P" + p.id, x + width / 2 - 5, chartHeight / 2);
        }
    }

    public static void main(String[] args) {
        // 创建一些示例进程
        List<Process> processes = new ArrayList<>();
        processes.add(new Process(1, 0, 5));
        processes.add(new Process(2, 1, 3));
        processes.add(new Process(3, 2, 2));
        processes.add(new Process(4, 4, 4));

        // 执行SJF抢占式算法
        List<Process> scheduledProcesses = new GanttChart().scheduleSJF(processes);

        // 创建甘特图窗口
        JFrame frame = new JFrame("SJF Preemptive Gantt Chart");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(800, 300);
        frame.add(new GanttChart(scheduledProcesses));
        frame.setVisible(true);
    }
}

常见问题

  1. 如何处理相同到达时间的进程?

    • 可以根据进程ID或其他规则进行排序,确保调度顺序的确定性。
  2. 如何处理进程的I/O操作?

    • 可以将I/O操作视为一个独立的进程,并在甘特图中进行表示。
  3. 如何优化甘特图的绘制效率?

    • 可以使用缓存机制或其他图形优化技术,提高绘制速度。
  4. 如何实现甘特图的交互功能?

    • 可以使用鼠标事件监听器,例如点击进程矩形显示详细信息。
  5. 如何将甘特图导出为图片?

    • 可以使用Java的图像处理库,例如ImageIO,将甘特图保存为PNG或JPEG格式。

通过本文的介绍,我们了解了如何使用Java绘制甘特图,并以SJF抢占式算法为例,展示了进程调度的可视化过程。希望本文能够帮助读者更好地理解进程调度算法,并掌握使用Java进行图形化编程的基本方法。