数据结构和算法专题---5、调度算法与应用

本章我们会对调度算法做个简单介绍,包括常用的调度算法(FCFS、SJF、RR、HPF)的概述、实现方式、典型场景做个说明。

什么是调度算法

调度算法常见于操作系统中,因为系统资源有限,当有多个进程(或多个进程发出的请求)要使用这些资源时,就必须按照一定的原则选择进程(请求)来占用资源。这就是所谓的调度。在现实生活中也是一样,比如会议室的占用。

先来先服务(FCFS)

概念

先来先服务,很好理解,就是按照服务提交申请的顺序,依次执行。讲究先来后到。

实现

定义一个Task类作为任务实例,BlockingQueue作为服务队列

package com.ls.cloud.sys.alg.schedule;/*** 任务类*/
public class Task {//任务名称private String name;//任务提交的时间private Long addTime;//任务的执行时间private int servTime;//任务优先级private int level;public Task(String name, int servTime) {this.name = name;this.servTime = servTime;this.addTime = System.currentTimeMillis();}public Task(String name, int servTime,int level) {this.name = name;this.servTime = servTime;this.level = level;this.addTime = System.currentTimeMillis();}public void execute() {try {// !重点:执行时睡眠,表示该任务耗时servTime毫秒Thread.currentThread().sleep(servTime);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(String.format("execute:name=%s,level=%s,addTime=%s,servTime=%s",name,level, addTime, servTime));}
}
package com.ls.cloud.sys.alg.schedule;import java.util.Random;
import java.util.concurrent.LinkedBlockingQueue;public class FCFS {public static void main(String[] args) throws InterruptedException {//阻塞队列,FCFS的基础final LinkedBlockingQueue<Task> queue = new LinkedBlockingQueue(5);//服务线程,任务由该线程获取和执行new Thread(new Runnable(){@Overridepublic void run() {while (true) {try {queue.take().execute();} catch (Exception e) {e.printStackTrace();}}}}).start();//向队列中每隔100ms防入一个任务for (int i = 0; i < 5; i++) {System.out.println("add task:"+i);queue.put(new Task("task"+i,new Random().nextInt(1000)));}}}

结果

add task:0
add task:1
add task:2
add task:3
add task:4
execute:name=task0,level=0,addTime=1701829693127,servTime=67
execute:name=task1,level=0,addTime=1701829693127,servTime=593
execute:name=task2,level=0,addTime=1701829693127,servTime=903
execute:name=task3,level=0,addTime=1701829693127,servTime=719
execute:name=task4,level=0,addTime=1701829693127,servTime=283
  • add按顺序放入,时间有序
  • execute也按时间顺序执行,而不管后面的servTime,也就是不管执行任务长短,先来先执行

优缺点

  • 多应用于cpu密集型任务场景,对io密集型的不利。
  • 时间相对均衡的业务可以排队处理,比如现实中排队打卡进站。
  • 如果业务需要依赖大量的外部因素,执行时间片长短不一,FCFS算法不利于任务的整体处理进度,可能会因为一个长时间业务的阻塞而造成大量等待。

短作业优先 (SJF)

概念

执行时间短的优先得到资源。即执行前申报一个我需要占据cpu的时间,根据时间长短,短的优先被调度。我不占时间所以我先来。

实现

使用TreeMap可以实现优先级的任务排序。

package com.ls.cloud.sys.alg.schedule;import javax.swing.text.html.parser.Entity;
import java.util.Map;
import java.util.Random;
import java.util.TreeMap;public class SJF {public static void main(String[] args) throws InterruptedException {//有序Map,将服务时间作为key排序final TreeMap<Integer,Task> treeMap = new TreeMap();//向队列中放入5个任务for (int i = 0; i < 5; i++) {System.out.println("add task:"+i);int servTime = new Random().nextInt(1000);//注意,key是servTime,即执行预估时间treeMap.put(servTime,new Task("task"+i,servTime));}//服务线程,任务由该线程获取和执行new Thread(new Runnable(){@Overridepublic void run() {while (true) {try {//有序Map中,服务时间短的,置于顶部,那么自然就会优先被取出Map.Entry<Integer,Task> entry = treeMap.pollFirstEntry();if (entry == null){Thread.currentThread().sleep(100);}else {entry.getValue().execute();}} catch (Exception e) {e.printStackTrace();}}}}).start();}
}

结果

add task:0
add task:1
add task:2
add task:3
add task:4
execute:name=task2,level=0,addTime=1701842880485,servTime=503
execute:name=task1,level=0,addTime=1701842880485,servTime=622
execute:name=task4,level=0,addTime=1701842880485,servTime=862
execute:name=task3,level=0,addTime=1701842880485,servTime=941
execute:name=task0,level=0,addTime=1701842880485,servTime=952

  • add任务有序,确实按照从前往后顺序提交的
  • execute任务无序,按servtime排序,说明执行时间段的得到了优先执行

优缺点

  • 适用于任务时间差别较大的场景,仍然以进站为例,拿出公交卡的优先刷卡,还没办卡的让一让。
  • 解决了FCFS整体处理时间长的问题,降低平均等待时间,提高了系统吞吐量。
  • 未考虑作业的紧迫程度,因而不能保证紧迫性作业(进程)的及时处理
  • 对长作业的不利,可能等待很久而得不到执行
  • 时间基于预估和申报,主观性因素在内,无法做到100%的精准

时间片轮转(RR)

概念

时间片逐个扫描轮询,轮到谁谁执行。大家公平裁决来者有份,谁也别插队。像是棋牌游戏中的发牌操作,做到了时间和机会上的平均性。

实现

基于数组做为数据插槽方式实现。

package com.ls.cloud.sys.alg.schedule;import java.util.Random;public class RR {//定义数组作为插槽,每个插槽中可以放入任务Integer[] integers;//length插槽的个数public RR(int length){integers = new Integer[length];}//将任务放入插槽public void addTask(int value){int slot = 0;//不停查找空的插槽while (true) {//发现空位,将当前任务放入if (integers[slot] == null){integers[slot] = value;System.out.println(String.format("------------------------->add task index=%s,value=%s",slot,value));break;}//如果当前位置有任务占用,看下一个位置slot++;//如果插槽遍历完还是没有空位置,那么从头开始再找,继续下一个轮回if (slot == integers.length){slot = 0;}}}//执行任务。轮询的策略就在这里public void execute(){//开启一个线程处理任务。在现实中可能有多个消费者来处理new Thread(new Runnable() {@Overridepublic void run() {int index = 0;while (true) {//指针轮询,如果到达尾部,下一步重新转向开头// 数据物理结构是一个数组,逻辑上是一个环if (index == integers.length){index = 0;}//如果当前位置没有任务,轮询到下一个插槽if (integers[index] == null){index++;continue;}else{//随机等待,表示模拟当前任务有一个执行时间try {Thread.currentThread().sleep(new Random().nextInt(1000));} catch (InterruptedException e) {e.printStackTrace();}//模拟任务执行的内容,也就是打印一下当前插槽和里面的值System.out.println(String.format("execute index=%s,value=%s",index,integers[index]));//执行完,将当前插槽清空,腾出位置来给后续任务使用integers[index] = null;}}}}).start();}public static void main(String[] args) {//测试开始,定义3个插槽RR rr = new RR(3);//唤起执行者线程,开始轮询rr.execute();//放置10个任务for (int i = 0; i < 10; i++) {rr.addTask(i);}}
}

结果

------------------------->add task index=0,value=0
------------------------->add task index=1,value=1
------------------------->add task index=2,value=2
execute index=0,value=0
------------------------->add task index=0,value=3
execute index=1,value=1
------------------------->add task index=1,value=4
execute index=2,value=2
------------------------->add task index=2,value=5
execute index=0,value=3
execute index=1,value=4
------------------------->add task index=1,value=6
------------------------->add task index=0,value=7
execute index=2,value=5
------------------------->add task index=2,value=8
execute index=0,value=7
execute index=1,value=6
------------------------->add task index=1,value=9
execute index=2,value=8
execute index=1,value=9
  • add任务index无序,value有序,说明是按顺序提交的,但是插槽无序,哪里有空放哪里
  • execute执行index有序value无序,说明任务是轮询执行的,每个插槽里的任务不一定是谁

优缺点

  • 做到了机会的相对平均,不会因为某个任务执行时间超长而永远得不到执行
  • 缺乏任务主次的处理。重要的任务无法得到优先执行,必须等到时间片轮到自己,着急也没用

优先级调度(HPF)

概述

进程调度每次将处理机分配给具有最高优先级的就绪进程。最高优先级算法可与不同的CPU方式结合形成可抢占式最高优先级算法和不可抢占式最高优先级算法。

实现

在Task类中新增一个属性level作为优先级标识

package com.ls.cloud.sys.alg.schedule;/*** 任务类*/
public class Task {//任务名称private String name;//任务提交的时间private Long addTime;//任务的执行时间private int servTime;//任务优先级private int level;public Task(String name, int servTime) {this.name = name;this.servTime = servTime;this.addTime = System.currentTimeMillis();}public Task(String name, int servTime,int level) {this.name = name;this.servTime = servTime;this.level = level;this.addTime = System.currentTimeMillis();}public void execute() {try {// !重点:执行时睡眠,表示该任务耗时servTime毫秒Thread.currentThread().sleep(servTime);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(String.format("execute:name=%s,level=%s,addTime=%s,servTime=%s",name,level, addTime, servTime));}
}
package com.ls.cloud.sys.alg.schedule;import java.util.Map;
import java.util.Random;
import java.util.TreeMap;public class HPF {public static void main(String[] args) throws InterruptedException {//有序Map,将服务优先级作为key排序final TreeMap<Integer, Task> treeMap = new TreeMap();//向队列中放入5个任务for (int i = 0; i < 5; i++) {System.out.println("add task:" + i);int servTime = new Random().nextInt(1000);//注意放入的key,是优先级,这里用i标识treeMap.put(i, new Task("task" + i, servTime,i));}//服务线程,任务由该线程获取和执行new Thread(new Runnable() {@Overridepublic void run() {while (true) {try {//有序Map中,优先级最高的,在底部部,那么自然就会优先被取出Map.Entry<Integer, Task> entry = treeMap.pollLastEntry();if (entry == null) {Thread.currentThread().sleep(100);} else {entry.getValue().execute();}} catch (Exception e) {e.printStackTrace();}}}}).start();}
}

结果

add task:0
add task:1
add task:2
add task:3
add task:4
execute:name=task4,level=4,addTime=1701843942807,servTime=452
execute:name=task3,level=3,addTime=1701843942807,servTime=433
execute:name=task2,level=2,addTime=1701843942807,servTime=196
execute:name=task1,level=1,addTime=1701843942807,servTime=166
execute:name=task0,level=0,addTime=1701843942806,servTime=97
  • 按0-4顺序加入task
  • 执行时,按4-0,优先级高的先得到执行,而与服务时间,加入的时间无关

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/210607.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Oracle 怎樣修改DB_NAME

DBNEWID 是一个数据库实用程序&#xff0c;用于更改 Oracle 数据库的 DBNAME 和 DBID。可以更改 DBID 或 DBNAME 或两者。 DBNAME 是在创建数据库时指定的数据库名称&#xff0c;DBID 是创建数据库时分配给数据库的唯一编号。 以下步骤演示如何使用 DBNEWID 实用程序更改 Oracl…

【论文阅读笔记】序列数据的数据增强方法综述

【论文阅读笔记】序列数据的数据增强方法综述 摘要 这篇论文探讨了在深度学习模型中由于对精度的要求不断提高导致模型框架结构变得更加复杂和深层的趋势。随着模型参数量的增加&#xff0c;训练模型需要更多的数据&#xff0c;但人工标注数据的成本高昂&#xff0c;且由于客观…

将RK3399的挖掘机开发板在Android10下设置系统默认为24小时制

将RK3399的挖掘机开发板在Android10下设置系统默认为24小时制 2023/12/9 22:07 应该也可以适用于RK3399的Android12系统 --- a/frameworks/base/packages/SettingsProvider/res/values/defaults.xml b/frameworks/base/packages/SettingsProvider/res/values/defaults.xml -2…

MagicAnimate

简介 新加坡国立大学 Show 实验室和字节联合做了一项类似的研究。他们提出了一个基于扩散的框架 MagicAnimate&#xff0c;旨在增强时间一致性、忠实地保留参考图像并提升动画保真度。并且&#xff0c;MagicAnimate 项目是开源的&#xff0c;目前推理代码和 gradio 在线 demo …

python程序大全(9)——鼠标乱动恶搞小病毒(有资源)

目录 &#x1f3c6;一、前言 &#x1f3c6;二、程序第一版 &#x1f3c6;三、程序大魔改 &#x1f6a9;1、基础改动 &#x1f6a9;2、打包 &#x1f6a9;3、F12保护机制 &#x1f6a9;4、添加开机自启项 &#x1f6a9;5、自己也不懂的线程魔改 &#x1f3c6;四、最终代码 &…

排列游戏 --- 动态规划 --- 题解

目录 排列游戏 题目描述 输入描述: 输出描述: 输入 输出 备注: 思路&#xff1a; 代码&#xff1a; 排列游戏 K-排列游戏_牛客竞赛动态规划专题班习题课 (nowcoder.com) 时间限制&#xff1a;C/C 1秒&#xff0c;其他语言2秒 空间限制&#xff1a;C/C 262144K&#…

外包干了三年,我承认我确实废了……

没错&#xff0c;我也干过外包&#xff0c;一干就是三年&#xff0c;三年后&#xff0c;我废了…… 虽说废的不是很彻底&#xff0c;但那三年我几乎是出差了三年、玩了三年、荒废了三年&#xff0c;那三年&#xff0c;我的技术能力几乎是零成长的。 说起这段三年的外包经历&a…

vue中滚轮缩放事件

在Vue中&#xff0c;可以使用原生JS的滚轮事件监听来实现滚轮缩放&#xff1a; 首先在模板中给需要监听滚轮事件的元素添加一个ref属性&#xff0c;用于在Vue中获取元素节点。 <template><div ref"scale"><!-- 需要缩放的内容 --></div> &…

Ubuntu中编译出Windows的可执行程序(.exe)

1、前言 在嵌入式开发中&#xff0c;交叉编译是很常见的情况&#xff0c;如果你把Windows电脑也看做一块高性能的开发板&#xff0c;那在Ubuntu中编译出Windows上运行的可执行程序也是很好理解的行为。 2、安装mingw64环境 sudo apt-get install mingw-w64 3、测试编译链是否安…

【力扣100】5.盛水最多的容器

添加链接描述 我的题解&#xff1a; class Solution:def maxArea(self, height: List[int]) -> int:# 两层for循环&#xff0c;保存最大值temp0res0for i in range(len(height)-1):for j in range(i1,len(height)):tempmin(height[i],height[j])*(j-i)# print(temp)resmax…

Linux压缩命令tar之排除不需要的文件或者目录(--exclude)

tar 中–exclude的简单用法 # 首先创建一个如下的目录结构和测试文件 mydir/ ├── myfile ├── zidir1 │ ├── file1 │ └── file2 ├── zidira │ └── filea └── zidirA├── fileA└── fileB3 directories, 6 files# 上面在 mydir 目录下有三个子…

C++知识点总结(8):尺取法

尺取法 一、复习枚举算法1. 算法三要素2. 最小公倍数公式3. 时间复杂度 二、算法优化初级1. 概念2. 例题(1) 最长小写子串Ⅰ 初步算法Ⅱ 认识尺取法Ⅲ 尺取法程序 (2) 最长递增子串(3) 最小子串和Ⅰ 伪代码Ⅱ 完整代码 (4) 最短字符串包含Ⅰ 伪代码 Ⅱ 代码 一、复习枚举算法 …

打破常规思维:Scrapy处理豆瓣视频下载的方式

概述 Scrapy是一个强大的Python爬虫框架&#xff0c;它可以帮助我们快速地开发和部署各种类型的爬虫项目。Scrapy提供了许多方便的功能&#xff0c;例如请求调度、数据提取、数据存储、中间件、管道、信号等&#xff0c;让我们可以专注于业务逻辑&#xff0c;而不用担心底层的…

MongoDB简介与安装

目录 1. MongoDB简介 2. 安装MongoDB 3. 基本命令行操作 4. Java代码实践 MongoDB是一种NoSQL数据库&#xff0c;以其灵活的文档存储模型和高度可扩展性而闻名。这篇文章将简单介绍一下MongoDB的基本概念&#xff0c;包括其特点和优势&#xff0c;并提供安装MongoDB的步骤。…

MapReduce的执行过程(以及其中排序)

Map阶段(MapTask)&#xff1a; 切片(Split)-----读取数据(Read)-------交给Mapper处理(Map)------分区和排序(sort) Reduce阶段(ReduceTask): 拷贝数据(copy)------排序(sort)-----合并(reduce)-----写出(write) 1、Map task读取&#xff1a; 框架调用InputFormat类的子类读取…

Vue2与Vue3的语法对比

Vue2与Vue3的语法对比 Vue.js是一款流行的JavaScript框架&#xff0c;通过它可以更加轻松地构建Web用户界面。随着Vue.js的不断发展&#xff0c;Vue2的语法已经在很多应用中得到了广泛应用。而Vue3于2020年正式发布&#xff0c;带来了许多新的特性和改进&#xff0c;同时也带来…

rpc原理与应用

IPC和RPC&#xff1f; RPC 而RPC&#xff08;Remote Procedure Call&#xff09;&#xff0c;又叫做远程过程调用。它本身并不是一个具体的协议&#xff0c;而是一种调用方式。 gRPC 是 Google 最近公布的开源软件&#xff0c;基于最新的 HTTP2.0 协议&#xff0c;并支持常见…

【SQLite】SQLite3约束总结

前面学习了SQLite数据库的常见使用方法&#xff0c;其中包含许多约束&#xff0c;常见的如NOT NULL、DEFAULT、UNIQUE、PRIMARY KEY&#xff08;主键&#xff09;、CHECK等 本篇文章主要介绍这些约束在SQLite中的使用 目录 什么是约束NOT NULL 约束DEFAULT约束UNIQUE约束PRIMA…

【设计模式-3.2】结构型——适配器模式

说明&#xff1a;本文介绍设计模式中结构型设计模式中的&#xff0c;适配器模式&#xff1b; 插头转换器 适配器模式属于结构型设计模式&#xff0c;设计思想体现在结构上的。以插头转换器为例&#xff0c;当你需要给手机充电&#xff0c;但是眼前只有一个三孔插座&#xff0…

Java基本类型的高级使用方法详解

引言 Java中的基本数据类型&#xff08;primitive types&#xff09;是构建程序的基础&#xff0c;包括整型、浮点型、字符型、布尔型等。除了直接使用这些基本类型外&#xff0c;Java还提供了一些高级的使用方法&#xff0c;使得我们能够更灵活地处理基本类型数据。本文将深入…