操作系统实验之多线程操作之读者优先与写者优先第二版
时间:2022-07-23
本文章向大家介绍操作系统实验之多线程操作之读者优先与写者优先第二版,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
之前作者做的那个实验有误,希望大家见谅,在室友的质疑之后觉得的确存在着很大的问题,所以自己今天又把一些逻辑上的漏洞又重新完善了一下。其实主要的逻辑漏洞又两个方面 第一就是,最后没有在总时间内到达的线程,最终是不能直接按照到达时间排序完之后的顺序直接打印的,而应该是每一次,将一个轮回里面能够到达的线程按照那样的检查机构打印出来后,剩下的没有到达的线程也应该是重新打入那个队列,然后在按照之前的规则继续打印,直到最后的队列中再也不存在元素,说明所有的线程都已经执行完毕 第二个就是之前的一个总时间计算问题,作者之后发现有一部分的总时间计算是存在问题的,下面作者会通过一个图来让读者们理解:
这样所有情况的图基本上都完成了。作者之前的第一版本,sum都考虑到了,但是在写者优先中漏了一种,就想着顺便把之前的图也给读者们讲一下吧。 接下来,是源代码。重点就是作者并不是在主函数中继续操作,而是通过将之前的那两种方法封装起来,通过队列是否为空来控制执行的过程
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Scanner;
public class 多线程编程第二版 {
public static Queue<tcb>queue=new PriorityQueue<tcb>();
public static List<tcb>list=new ArrayList<>();
public static int sum;
public static void duzhe()
{
//System.out.println("重新开始的队列");
tcb tcb2=queue.poll();
System.out.println(tcb2.id+" "+tcb2.name+"结束线程");
int n=queue.size();
sum=tcb2.starttime+tcb2.lasttime;
if(tcb2.name.equals("R"))//在第一个线程是读的情况下
{
for(int i=0;i<n;i++)//开始查询在第一个读线程执行的过程中是否有别的读线程进来,
{
tcb tcb3=queue.poll();
if(tcb3.name.equals("R"))//那么可以直接并发的执行,
{
if(tcb3.starttime<=sum)
{
System.out.println(tcb3.id+" "+tcb3.name+"结束线程");
if(sum<(tcb3.starttime+tcb3.lasttime))
sum=tcb3.starttime+tcb3.lasttime;
}
else //如果不在时间段内到达,就存入列表中,因为一开始的队列就已经按到达时间进行排序了
list.add(tcb3);
}
else {//因为读线程正在执行,所以写线程不能执行,也像那些没有在规定时间内到达的读线程一样存入列表中
list.add(tcb3);
}
}
for(int i=0;i<list.size();i++)
queue.add(list.get(i));
list.clear();
}
else//首个线程是写线程
{
a:for(int i=0;i<n;i++)//这里的重点是找出第一个读线程,因为只有读线程是存在这并发执行的情况的,其他的情况都是按照时间进行单向操作的
{
tcb tcb3=queue.poll();
if(tcb3.name.equals("W"))//写线程是单向执行的所以可以直接进行操作
{
System.out.println(tcb3.id+" "+tcb3.name+"结束线程");
if(sum>tcb3.starttime)
sum+=tcb3.lasttime;
else
sum=tcb3.starttime+tcb3.lasttime;
}
if(tcb3.name.equals("R"))//当找到第一个写线程是退出该循环,但是这个写线程是可以直接进行操作的
{
System.out.println(tcb3.id+" "+tcb3.name+"结束线程");
if(sum>tcb3.starttime)
sum+=tcb3.lasttime;
else
sum=tcb3.starttime+tcb3.lasttime;
break a;
}
}
int length=queue.size();
for(int i=0;i<length;i++)//这时候进行的操作仍就是类似于上面的把能够并发执行的线程都直接打印出来,不能并发执行的线程都存入列表中
{
tcb tcb3=queue.poll();
if(tcb3.name.equals("R"))
{
if(tcb3.starttime<=sum)
{
System.out.println(tcb3.id+" "+tcb3.name+"结束线程");
if(sum<(tcb3.starttime+tcb3.lasttime))
sum=tcb3.starttime+tcb3.lasttime;
}
else
list.add(tcb3);
}
else
{
list.add(tcb3);
}
}
for(int i=0;i<list.size();i++)
queue.add(list.get(i));
list.clear();
}
}
public static void xiezhe()
{
//System.out.println("开始新的队列");
tcb tcb2=queue.poll();
System.out.println(tcb2.id+" "+tcb2.name+"结束线程");
sum=tcb2.starttime+tcb2.lasttime;
int n=queue.size();
if(tcb2.name.equals("W"))//写着优先中如果第一个线程是写线程的话,那么操作就和上述读者优先的操作有点类似
{
for(int i=0;i<n;i++)
{
tcb tcb3=queue.poll();
if(tcb3.name.equals("W"))//这里可以直接打印写线程是因为写线程的优先级比读线程的优先级高
//而且这里的打印并不代表是并发的执行多个写线程,而是单向的一个一个执行写线程
{
if(tcb3.starttime<=sum)
{
System.out.println(tcb3.id+" "+tcb3.name+"结束线程");
sum+=tcb3.lasttime;
}
else //将不再能到达的范围内的写线程存入列表中
list.add(tcb3);
}
else
{
list.add(tcb3);
}
}
for(int i=0;i<list.size();i++)
queue.add(list.get(i));
list.clear();
}
else//这里的可以和上面的读者优先的第二种情况进行类比
{
a:for(int i=0;i<n;i++)
{
tcb tcb3=queue.poll();
if (tcb3.name.equals("W"))
{
System.out.println(tcb3.id+" "+tcb3.name+"结束线程");
//sum+=tcb3.lasttime;
if(sum>tcb3.starttime)
sum+=tcb3.lasttime;
else
sum=tcb3.starttime+tcb3.lasttime;
break a;
}
else
{
if(tcb3.starttime<=sum)
{
System.out.println(tcb3.id+" "+tcb3.name+"结束线程");
if(sum<(tcb3.starttime+tcb3.lasttime))
sum=tcb3.starttime+tcb3.lasttime;
}
}
}
int length=queue.size();
for(int i=0;i<length;i++)
{
tcb tcb3=queue.poll();
if(tcb3.name.equals("W"))
{
if(tcb3.starttime<=sum)
{
System.out.println(tcb3.id+" "+tcb3.name+"结束线程");
sum+=tcb3.lasttime;
}
else
list.add(tcb3);
}
else
{
list.add(tcb3);
}
}
for(int i=0;i<list.size();i++)
queue.add(list.get(i));
list.clear();
}
}
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
tcb[]tcb1=new tcb[n];
System.out.println("线程的id号 名称 开始时间 持续时间");
for(int i=0;i<n;i++)
{
tcb1[i]=new tcb();
tcb1[i].id=sc.nextInt();
//System.out.println("第"+(i+1)+"个线程的名称");
tcb1[i].name=sc.next();
//System.out.println("第"+(i+1)+"个线程的开始时间");
tcb1[i].starttime=sc.nextInt();
//System.out.println("第"+(i+1)+"个线程的持续时间");
tcb1[i].lasttime=sc.nextInt();
}
System.out.println("信号量机制如下:");
System.out.println("1.读者优先");
System.out.println("2.写者优先");
System.out.println("请选择机制的编号:");
int m=sc.nextInt();
while(m>2||m<1)
{
System.out.println("选择方式不存在,请重新选择机制编号:");
m=sc.nextInt();
}
if(m==1)//读者优先
{
sum=0;
queue=new PriorityQueue<tcb>(compare1);
for(int i=0;i<n;i++)
queue.add(tcb1[i]);
while(!queue.isEmpty())
{
duzhe();
}
}
else if(m==2)//写者优先
{
sum=0;
queue=new PriorityQueue<tcb>(compare1);
for(int i=0;i<n;i++)
queue.add(tcb1[i]);
while(!queue.isEmpty())
{
xiezhe();
}
}
}
static Comparator<tcb>compare1=new Comparator<tcb>() {//按到达时间排序
@Override
public int compare(tcb o1, tcb o2) {
// TODO Auto-generated method stub
return o1.starttime-o2.starttime;
}
};
static class tcb
{
int id;
String name;
int starttime;
int lasttime;
public tcb() {
// TODO Auto-generated constructor stub
}
}
}
最后作者提供了两个用例测试了两个版本的答案,最后的确证明逻辑上是存在漏洞的,下面分别是两个代码执行的结果图 首先是读者优先 正确的答案:
错误的答案演示:
之后是写者优先的演示 正确的答案演示:
错误的答案演示:
作者很菜,如果还有错误,还望大家指正!!!
- WebSocket在ASP.NET MVC4中的简单实现
- 在ASP.NET MVC中使用Unity进行依赖注入的三种方式第一种方法第二种方法第三种方法
- Unity Container中的几种注册方式与示例1.实例注册2.简单类型注册
- 使用Unity创建依赖注入依赖注入生命周期:注册、解析、销毁 注册解析销毁
- 使用WCF进行跨平台开发之三(JAVA调用WCF服务)1.开发必备2.生成WCF客户端3.开发程序4.结束语
- 使用WCF进行跨平台开发之二(IIS托管WCF服务并使用php平台调用)1.系统必备2.在IIS中托管WCF服务3.使用PHP调用托管在IIS中的WCF服务
- 使用WCF进行跨平台开发之一(WCF的实现、控制台托管与.net平台的调用)1.创建项目结构2.契约的设计3.实现服务4.控制台托管服务5.在.net平台中调用WCF
- 使用GUI工具高效构建你自己的Nuget包丰富包的基础信息添加要包含的文件The end
- 网页结构与表现原则
- CSS3动画功能
- Angular开发者手册重点翻译之指令(一)文本和属性绑定ngAttr属性绑定
- CSS3中的变形处理
- WCF中操作的分界于调用顺序和会话的释放操作分界实例停止
- WCF中数据契约之已知类型的几种公开方式代码中定义配置中定义宿主端使用解析器
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 修改Laravel自带的认证系统的User类的命名空间的步骤
- PHP使用递归按层级查找数据的方法
- Laravel6.2中用于用户登录的新密码确认流程详解
- PHP连接MySQL数据库的三种方式实例分析【mysql、mysqli、pdo】
- 浅谈PHP array_search 和 in_array 函数效率问题
- 仿抖音短视频APP源码,实现简单的换头像并保存
- php实现JWT(json web token)鉴权实例详解
- laravel实现上传图片,并且制作缩略图,按照日期存放的代码
- 在Laravel中使用MongoDB的方法示例
- 基于thinkphp6.0的success、error实现方法
- Yii框架模拟组件调用注入示例
- 解决laravel 表单提交-POST 异常的问题
- laravel5.0在linux下解决.htaccess无效和去除index.php的问题
- laravel返回统一格式错误码问题
- 使用 PHP Masked Package 屏蔽敏感数据的实现方法