Java 中用到的线程调度方式以及调度算法

线程调度方式

抢占式调度
抢占式调度指的是每条线程执行的时间、线程的切换都由系统控制,系统控制指的是在系统某种运行机制下,可能每条线程都分同样的执行时间片,也可能是某些线程执行的时间片较长,甚至某些线程得不到执行的时间片。在这种机制下,一个线程的堵塞不会导致整个进程堵塞。

协同式调度
协同式调度指某一线程执行完后主动通知系统切换到另一线程上执行,这种模式就像接力赛一样,一个人跑完自己的路程就把接力棒交接给下一个人,下个人继续往下跑。线程的执行时间由线程本身控制,线程切换可以预知,不存在多线程同步问题,但它有一个致命弱点:如果一个线程编写有问题,运行到一半就一直堵塞,那么可能导致整个系统崩溃。
 

左为抢占式,右为协同式

JVM 的线程调度实现(抢占式调度)

java 使用的线程调使用抢占式调度, Java 中线程会按优先级分配 CPU 时间片运行, 且优先级越高越优先执行,但优先级高并不代表能独自占用执行时间片,可能是优先级高得到越多的执行时间片,反之,优先级低的分到的执行时间少但不会分配不到执行时间。
 

线程让出 cpu 的情况
1. 当前运行线程主动放弃 CPU, JVM 暂时放弃 CPU 操作(基于时间片轮转调度的 JVM 操作系统不会让线程永久放弃 CPU,或者说放弃本次时间片的执行权),例如调用 yield()方法。
2. 当前运行线程因为某些原因进入阻塞状态,例如阻塞在 I/O 上。
3. 当前运行线程结束,即运行完 run()方法里面的任务。
 

调度算法

一、优先调度算法

1、先来先服务调度算法(FCFS)

当在作业调度中采用该算法时,每次调度都是从后备作业队列中选择一个或多个最先进入该队列的作业,将它们调入内存,为它们分配资源、创建进程,然后放入就绪队列。在进程调度中采用 FCFS 算法时,则每次调度是从就绪队列中选择一个最先进入该队列的进程,为之分配处理机,使之投入运行。该进程一直运行到完成或发生某事件而阻塞后才放弃处理机, 特点是:算法比较简单,可以实现基本上的公平。

2、短作业(进程)优先调度算法

短作业优先(SJF)的调度算法是从后备队列中选择一个或若干个估计运行时间最短的作业,将它们调入内存运行。而短进程优先(SPF)调度算法则是从就绪队列中选出一个估计运行时间最短的进程,将处理机分配给它,使它立即执行并一直执行到完成,或发生某事件而被阻塞放弃处理机时再重新调度。 该算法未照顾紧迫型作业。
 

二、高优先权优先调度算法

为了照顾紧迫型作业,使之在进入系统后便获得优先处理,引入了最高优先权优先(FPF)调度算法。当把该算法用于作业调度时,系统将从后备队列中选择若干个优先权最高的作业装入内存。当用于进程调度时,该算法是把处理机分配给就绪队列中优先权最高的进程。

1、非抢占式优先权算法
在这种方式下,系统一旦把处理机分配给就绪队列中优先权最高的进程后,该进程便一直执行下去,直至完成;或因发生某事件使该进程放弃处理机时。这种调度算法主要用于批处理系统中;也可用于某些对实时性要求不严的实时系统中。

2、抢占式优先权调度算法

在这种方式下,系统同样是把处理机分配给优先权最高的进程,使之执行。 但在其执行期间,只要又出现了另一个其优先权更高的进程进程调度程序就立即停止当前进程(原优先权最高的进程)的执行,重新将处理机分配给新到的优先权最高的进程。显然,这种抢占式的优先权调度算法能更好地满足紧迫作业的要求,故而常用于要求比较严格的实时系统中,以及对性能要求较高的批处理和分时系统中。

3、高响应比优先调度算法

在批处理系统中,短作业优先算法是一种比较好的算法,其主要的不足之处是长作业的运行得不到保证。如果我们能为每个作业引入前面所述的动态优先权,并使作业的优先级随着等待时间的增加而以速率 a 提高,则长作业在等待一定的时间后,必然有机会分配到处理机。该优先权的变化规律可描述为:

(1) 如果作业的等待时间相同,则要求服务的时间愈短,其优先权愈高,因而该算法有利于短作业。
(2) 当要求服务的时间相同时,作业的优先权决定于其等待时间,等待时间愈长,其优先权愈高,因而它实现的是先来先服务。
(3) 对于长作业,作业的优先级可以随等待时间的增加而提高,当其等待时间足够长时,其优先级便可升到很高,从而也可获得处理机。简言之,该算法既照顾了短作业,又考虑了作业到达的先后次序,不会使长作业长期得不到服务。因此,该算法实现了一种较好的折衷。当然,在利用该算法时,每要进行调度之前,都须先做响应比的计算,这会增加系统开销。

三、基于时间片的轮转调度算法

1、时间片轮转法

在早期的时间片轮转法中,系统将所有的就绪进程按先来先服务的原则排成一个队列,每次调度时,把 CPU 分配给队首进程,并令其执行一个时间片。时间片的大小从几 ms 到几百 ms。 当执行的时间片用完时,由一个计时器发出时钟中断请求,调度程序便据此信号来停止该进程的执行,并将它送往就绪队列的末尾;然后,再把处理机分配给就绪队列中新的队首进程,同时也让它执行一个时间片。 这样就可以保证就绪队列中的所有进程在一给定的时间内均能获得一时间片的处理机执行时间。

2.、多级反馈队列调度算法

(1) 应设置多个就绪队列,并为各个队列赋予不同的优先级。第一个队列的优先级最高,第二个队列次之,其余各队列的优先权逐个降低。该算法赋予各个队列中进程执行时间片的大小也各不相同,在优先权愈高的队列中,为每个进程所规定的执行时间片就愈小。例如,第二个队列的时间片要比第一个队列的时间片长一倍,……,第 i+1 个队列的时间片要比第 i 个队列的时间片长一倍。
(2) 当一个新进程进入内存后,首先将它放入第一队列的末尾,按 FCFS 原则排队等待调度。当轮到该进程执行时,如它能在该时间片内完成,便可准备撤离系统;如果它在一个时间片结束时尚未完成,调度程序便将该进程转入第二队列的末尾,再同样地按 FCFS 原则等待调度执行;如果它在第二队列中运行一个时间片后仍未完成,再依次将它放入第三队列,……,如此下去,当一个长作业(进程)从第一队列依次降到第 n 队列后,在第 n 队列便采取按时间片轮转的方式运行。
(3) 仅当第一队列空闲时,调度程序才调度第二队列中的进程运行;仅当第 1~ (i-1)队列均空时,才会调度第 i 队列中的进程运行。如果处理机正在第 i 队列中为某进程服务时,又有新进程进入优先权较高的队列(第 1~ (i-1)中的任何一个队列),则此时新进程将抢占正在运行进程的处理机,即由调度程序把正在运行的进程放回到第 i 队列的末尾,把处理机分配给新到的高优先权进程。在多级反馈队列调度算法中,如果规定第一个队列的时间片略大于多数人机交互所需之处理时间时,便能够较好的满足各种类型用户的需要。
 

  • 3
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
下面是在Java用多种方法实现SJF算法的示例代码: 方法一:使用数组实现 ```java public class SJF { public static void main(String[] args) { // 进程列表 String[] processNames = {"P1", "P2", "P3", "P4", "P5"}; int[] runTimes = {3, 2, 1, 4, 2}; int[] arriveTimes = {0, 1, 2, 3, 4}; int n = processNames.length; // 进程数量 int[] status = new int[n]; // 进程状态,0表示未到达,1表示已到达,2表示已完成 int[] remainTimes = new int[n]; // 剩余运行时间 // 初始化 for (int i = 0; i < n; i++) { status[i] = 0; remainTimes[i] = runTimes[i]; } int currTime = 0; // 当前时间 int finishedCount = 0; // 已完成进程数量 // 运行进程 while (finishedCount < n) { int shortestJob = -1; // 最短作业的进程编号 int shortestTime = Integer.MAX_VALUE; // 最短作业的运行时间 for (int i = 0; i < n; i++) { if (status[i] == 1 && remainTimes[i] < shortestTime) { shortestJob = i; shortestTime = remainTimes[i]; } } if (shortestJob == -1) { currTime++; continue; } System.out.println("Time " + currTime + ": " + processNames[shortestJob] + " is running"); remainTimes[shortestJob]--; currTime++; if (remainTimes[shortestJob] == 0) { System.out.println("Time " + currTime + ": " + processNames[shortestJob] + " is finished"); status[shortestJob] = 2; finishedCount++; } else if (arriveTimes[shortestJob+1] <= currTime) { status[shortestJob+1] = 1; } } } } ``` 方法二:使用链表实现 ```java public class SJF { static class Process { String name; int runTime; int arriveTime; Process(String name, int runTime, int arriveTime) { this.name = name; this.runTime = runTime; this.arriveTime = arriveTime; } } static class Node { Process process; Node next; Node(Process process) { this.process = process; this.next = null; } } static class LinkedList { Node head; LinkedList() { this.head = null; } // 插入节点 void insert(Process process) { Node newNode = new Node(process); if (head == null) { head = newNode; return; } if (process.runTime < head.process.runTime) { newNode.next = head; head = newNode; return; } Node curr = head; while (curr.next != null && curr.next.process.runTime < process.runTime) { curr = curr.next; } newNode.next = curr.next; curr.next = newNode; } // 删除节点 void delete(Node node) { if (node == head) { head = head.next; return; } Node curr = head; while (curr.next != node) { curr = curr.next; } curr.next = node.next; } } public static void main(String[] args) { // 进程列表 Process[] processes = { new Process("P1", 3, 0), new Process("P2", 2, 1), new Process("P3", 1, 2), new Process("P4", 4, 3), new Process("P5", 2, 4) }; LinkedList processList = new LinkedList(); // 初始化 for (Process process : processes) { processList.insert(process); } int currTime = 0; // 当前时间 int finishedCount = 0; // 已完成进程数量 Node currNode = processList.head; // 当前进程节点 // 运行进程 while (finishedCount < processes.length) { if (currNode == null) { currTime++; continue; } System.out.println("Time " + currTime + ": " + currNode.process.name + " is running"); currNode.process.runTime--; currTime++; if (currNode.process.runTime == 0) { System.out.println("Time " + currTime + ": " + currNode.process.name + " is finished"); processList.delete(currNode); finishedCount++; } else if (currNode.next != null && currNode.next.process.arriveTime <= currTime) { Node nextNode = currNode.next; processList.delete(nextNode); processList.insert(nextNode.process); } currNode = processList.head; } } } ``` 方法三:使用优先队列实现 ```java import java.util.PriorityQueue; public class SJF { static class Process implements Comparable<Process> { String name; int runTime; int arriveTime; Process(String name, int runTime, int arriveTime) { this.name = name; this.runTime = runTime; this.arriveTime = arriveTime; } @Override public int compareTo(Process o) { return Integer.compare(runTime, o.runTime); } } public static void main(String[] args) { // 进程列表 Process[] processes = { new Process("P1", 3, 0), new Process("P2", 2, 1), new Process("P3", 1, 2), new Process("P4", 4, 3), new Process("P5", 2, 4) }; PriorityQueue<Process> processQueue = new PriorityQueue<>(); for (Process process : processes) { processQueue.offer(process); } int currTime = 0; // 当前时间 int finishedCount = 0; // 已完成进程数量 // 运行进程 while (finishedCount < processes.length) { if (processQueue.isEmpty()) { currTime++; continue; } Process currProcess = processQueue.poll(); System.out.println("Time " + currTime + ": " + currProcess.name + " is running"); currProcess.runTime--; currTime++; if (currProcess.runTime == 0) { System.out.println("Time " + currTime + ": " + currProcess.name + " is finished"); finishedCount++; } else { for (Process process : processes) { if (process.arriveTime <= currTime && !processQueue.contains(process)) { processQueue.offer(process); } } processQueue.offer(currProcess); } } } } ``` 以上三种方法都可以实现SJF算法,方法一使用数组,方法二使用链表,方法三使用优先队列。其,使用优先队列实现最简单,但是需要重写进程类的 `compareTo` 方法。使用数组和链表实现需要手动实现进程的排序和插入操作,但是可以更灵活地控制进程的顺序。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值