操作系统之进程调度——优先权法和轮转法(附上样例讲解)
操作系统之银行家算法—详解流程及案例数据
操作系统之多线程编程—读者优先/写者优先详解
操作系统之存储管理——FIFO算法和LRU算法
操作系统之磁盘调度——SCAN实例讲解

要求

一、实验目的
1、熟悉多线程编程
2、熟悉使用信号量机制解决进程同步问题
二、实验内容
创建一个包含n 个线程的控制台进程。用这n 个线程来表示n个读者或写者。每个线程按相应测试数据文件的要求,进行读写操作。请用信号量机制分别实现读者优先和写者优先的读者-写者问题。
读者优先:如果一个读者申请进行读操作时已有另一读者正在进行读操作,则该读者可直接开始读操作。
写者优先:如果一个读者申请进行读操作时已有另一写者在等待访问共享资源,则该读者必须等到没有写者处于等待状态后才能开始读操作。
三、实验条件
1、为每个学生提供一台具有WINDOWS 2000/NT/XP操作系统的计算机;
2、实验机器要求安装Visual C 6.0编程平台;
3、实验要求一人一机。
四、 运行结果显示要求
1、要求在每个线程创建、发出读写操作申请、开始读写操作和结束读写操作时分别显示一行提示信息,以确信所有处理都遵守相应的读写操作限制。
2、测试数据文件格式:测试数据文件包括n 行测试数据,分别描述创建的n 个线程是读者还是写者,以及读写操作的开始时间和持续时间。每行测试数据包括四个字段,各字段间用空格分隔。第一字段为一个正整数,表示线程序号。第二字段表示相应线程角色,R 表示读者是,W 表示写者。第三字段为一个正数,表示读写操作的开始时间。线程创建后,延时相应时间(单位为秒)后发出对共享资源的读写申请。第四字段为一个正数,表示读写操作的持续时间。当线程读写申请成功后,开始对共享资源的读写操作,该操作持续相应时间后结束,并释放共享资源。下面是一个测试数据文件的例子:
1 R 3 5
2 W 4 5
3 R 5 2
4 R 6 5

理解

上面是实验要求。下面讲讲自己的体会:

  • 首先要明白在多线程编程中的互斥关系——读写互斥写写互斥。两个优先方式都遵从这个出发点。
  • 在满足以上的条件下,进来的线程都好像是在排队,然后看当前谁优先。
  • 自己如果是优先的,那么可以直接插队。再考虑正在执行的是否和自己阻塞,如果正在执行的和自己阻塞,那自己也得排队,只不过排在前面。(类似写者优先的写写线程)如果正在执行的和自己不是阻塞的,那么自己就可以直接进去执行(读者优先的读读进程)。
  • 如果自己不是优先的,那么自己只能老老实实的排队,就算自己前面有和自己不互斥的线程执行也不行。类似写者优先的(读线程在执行,新的写进程等待资源,更新的读线程只能等写进程释放,如果有新的写进程进来,还可以插队,——他只能等待队列中所有在等待的写进程释放完毕再执行自己。)
  • 对于读者优先

读者就是优先的。假设a,b都是同时请求,但是a是读者那么a优先使用资源,还有一点很重要的就是读者优先的读者可以并行执行。而写着只能单线程执行。在执行过程中,只要阻塞的写者在等待过程中有新的读者进来那么他要等待所有读者完成才能自己释放自己。

  • 对于写者优先

无疑所有写的操作是优先的,这个过程可能会产生大量阻塞,因为相对较快(本来可以并行的读者被大量阻塞)。如果资源中没有写者那么读者依然可以并行,但是一旦出现写者在等待读者资源,那么新的读者就不能在并行执行,要等待所有写者执行完毕才可执行读者。

对于多线程编程的注意点有:

  1. 读者优先和写者优先是两个不同的策略方法,方法有相似之处但是也有很大不同,函数需要分开完成。
  2. 最主要的排序方式基于时间排序,次要的排序以读者还是写者谁优先为准则
  3. 读者优先或者写者优先的阻塞会导致线程开始时间的变化。而不过采用双队列一个存进入时间的排序,一个存结束时间的排序,修改其中的一个会影响另一个队列中元素值不错,但是如果不对另一个队列进行增/删不会触发堆排序的功能(挺重要的)。
  4. 可能有些阻塞时候的等待时间和开始时间改变处理比较复杂,要考虑当前是读致使阻塞,还是写致使阻塞,还是前面有写的资源再等待致使阻塞。要用多个变量维系系统使得正确的更改线程的阻塞时间。

我的大体思路(水平有限,不一定很准确):

代码

下面附上我的ac代码

package 实验;import java.util.Comparator;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Scanner;public class morethread {static Scanner sc=new Scanner(System.in);static Queue<thread>q1;static Queue<thread>end;static thread thread[];static int time=0;private static void threadcreate(int n) {//创建线程thread=new thread[n];for(int i=0;i<n;i++) {System.out.println("请输入线程"+(i+1)+"的相关属性(id,r/w,intime,timelong)");int id=sc.nextInt();String type=sc.next();int start=sc.nextInt();int chixu=sc.nextInt();thread [i]=new thread(id, type, start, chixu);System.out.println("线程"+id+"  操作:"+type+"  开始时间:"+start+"   持续时间:"+chixu);}}private static void writerthread(int n) {//写者优先// TODO 自动生成的方法存根q1=new PriorityQueue<thread>(comstart2);end=new PriorityQueue<thread>(comend);for(int i=0;i<n;i++){q1.add(thread[i]);end.add(thread[i]);}int timeend=-1;//读结束的时间最晚int writendtime=0;//写操作结束的时间thread start=q1.peek();thread finish=end.peek();boolean iswait=false;//是否有write操作在等待while(!q1.isEmpty()||!end.isEmpty()){if(!q1.isEmpty()) {start=q1.peek();}if(!end.isEmpty()) {finish=end.peek();}if(start.starttime<(finish.starttime+finish.totaltime)&&!q1.isEmpty())//可以开始读写操作{thread team=q1.poll();if(team.type.equals("w"))//写操作{if(team.starttime>=writendtime)//没有写操作在前面{if(team.starttime<timeend)//需要等待 更改改点信息重新pull入队列{iswait=true;//声明之后其他读操作就不能再直接进行team.starttime=timeend;q1.add(team);end.remove(team);end.add(team);}else {//不需要等待,直接开始System.out.println("线程"+team.id+" 开始:"+team.type+"操作  时间:"+team.starttime+"  结束时间"+(team.starttime+team.totaltime));writendtime=team.starttime+team.totaltime;//更新写操作的结束时间iswait=false;//更新等待点}}else//有正在进行的操作,需要等待{iswait=true;team.starttime=writendtime;q1.add(team);end.remove(team);end.add(team);//移除然后增加才可以重新排序}}else//读操作{if(iswait){//不清楚他在等待读操作还是写操作,反正它一定不能在下面两个操作之前完成,所以更新节点道值最大的int time2=timeend;if(time2<writendtime)time2=writendtime;team.starttime=time2;q1.add(team);end.remove(team);end.add(team);}else//没有等待点{if(team.starttime<writendtime)//有写操作,读需要等待 将这个点从新弄道队列里面{team.starttime=writendtime;q1.add(team);end.remove(team);end.add(team);}else//可以释放了{System.out.println("线程"+team.id+" 开始:"+team.type+"操作  时间:"+team.starttime+"  结束时间"+(team.starttime+team.totaltime));    if(team.starttime+team.totaltime>timeend)//如果可以更新时间{timeend=team.starttime+team.totaltime;}}}}                }   else{thread team=end.poll();System.out.println("线程"+team.id+" 结束:"+team.type+" 结束时间"+(team.starttime+team.totaltime));}}}private static void readerthread(int n)//读者优先{q1=new PriorityQueue<thread>(comstart);end=new PriorityQueue<thread>(comend);for(int i=0;i<n;i++){q1.add(thread[i]);end.add(thread[i]);}int timeend=-1;//读结束的时间最晚int writendtime=0;//写操作结束的时间,如果有写的状态,那么thread start=q1.peek();thread finish=end.peek();while(!q1.isEmpty()||!end.isEmpty()){if(!q1.isEmpty()) {start=q1.peek();}if(!end.isEmpty()) {finish=end.peek();}if(start.starttime<=(finish.starttime+finish.totaltime)&&!q1.isEmpty())//可以开始读写操作{thread team=q1.poll();if(writendtime>start.starttime)//此时有写的情况,需要等待阻塞,也就是将此队列头抛出修改开始时间然后再次入队{team.starttime=writendtime;q1.add(team);end.remove(team);end.add(team);}else {//此时无写的操作,无操作或者读操作//System.out.println("线程"+team.id+" 开始:"+team.type+"操作  时间:"+team.starttime+"  结束时间"+(team.starttime+team.totaltime));if(team.type.equals("w"))//写的情况,写需要检查是否阻塞自己如果有读的情况则需要阻塞自己{int time2=timeend;if(time2<writendtime)time2=writendtime;if(team.starttime>=time2)//在这个时间前没有任何读或者写的操作,上锁,等于号一定有,因为都抛出它了,他的优先级一定最高{writendtime=team.starttime+team.totaltime;System.out.println("线程"+team.id+" 开始:"+team.type+"操作  时间:"+team.starttime+"  结束时间"+(team.starttime+team.totaltime));}else {//状态还有读操作,需要阻塞team.starttime=time2;q1.add(team);end.remove(team);end.add(team);}}else//读的情况{System.out.println("线程"+team.id+" 开始:"+team.type+"操作  时间:"+team.starttime+"  结束时间"+(team.starttime+team.totaltime));if(team.starttime+team.totaltime>timeend){timeend=team.starttime+team.totaltime;}}}}else if(!end.isEmpty()){//某个结束操作在这个时间段thread team=end.poll();System.out.println("线程"+team.id+" 结束:"+team.type+" 结束时间"+(team.starttime+team.totaltime));}}}public static void main(String[] args) {System.out.println("请输入n个线程程序");      int n=sc.nextInt();threadcreate(n);System.out.println("请输入数字选择读者优先或者写者优先\n1:读者优先\n2:写者优先");int index=sc.nextInt();while(index<1||index>2) {System.out.println("请输入正确的数字");index=sc.nextInt();}if(index==1)//读者优先{readerthread(n);}else {writerthread(n);}}static Comparator<thread>comstart=new Comparator<thread>() {//读者优先的comparator接口,优先时间,时间相同则先返回public int compare(thread o1, thread o2) {// TODO 自动生成的方法存根if(o1.starttime==o2.starttime){return (int)(o1.type.charAt(0)-o2.type.charAt(0));// r w  先r后w}elsereturn o1.starttime-o2.starttime;//返回小根堆            }};static Comparator<thread>comstart2=new Comparator<thread>() {//写者优先的public int compare(thread o1, thread o2) {// TODO 自动生成的方法存根if(o1.starttime==o2.starttime){return (int)(o2.type.charAt(0)-o1.type.charAt(0));// w r  先w后r}elsereturn o1.starttime-o2.starttime;//返回小根堆         }};static Comparator<thread>comend=new Comparator<thread>() {public int compare(thread o1, thread o2) {return (int)((o1.starttime+o1.totaltime)-(o2.starttime+o2.totaltime));}};static class thread{int id;String type;int starttime;int totaltime;public thread(int id,String type,int starttime,int totaltime){this.id=id;this.type=type;this.starttime=starttime;this.totaltime=totaltime;}}}

测试用例:

读者优先:


写者优先:

本人水平有限,?,如果有大佬发现错了,请指正。

如果对后端、爬虫、数据结构算法等感性趣欢迎关注我的个人公众号交流:bigsai

操作系统之多线程编程—读者优先/写者优先详解相关推荐

  1. 深入浅出多线程编程实战(五)ThreadLocal详解(介绍、使用、原理、应用场景)

    深入浅出多线程编程实战(五)ThreadLocal详解(介绍.使用.原理.应用场景) 文章目录 一.ThreadLocal简介 二.ThreadLocal与Synchronized区别 三.Threa ...

  2. java 读者写者_Java实现生产者消费者问题与读者写者问题详解

    1.生产者消费者问题 生产者消费者问题是研究多线程程序时绕不开的经典问题之一,它描述是有一块缓冲区作为仓库,生产者可以将产品放入仓库,消费者则可以从仓库中取走产品.解决生产者/消费者问题的方法可分为两 ...

  3. Java网络编程(6)NIO - Channel详解

    前言 NIO的三个核心组件:Buffer.Channel.Selector Java网络编程(4)NIO的理解与NIO的三个组件完成了大概的了解 Java网络编程(5)NIO - Buffer详解详细 ...

  4. shell编程数组与冒泡算法排序详解

    shell编程数组与冒泡算法排序详解 一 数组的四种表达方式 二 数组的增删改查操作 三 数组传参 3.1 主程序向函数传数组参数 3.2 从函数返回数组到主程序 四 冒泡算法排序 一 数组的四种表达 ...

  5. shell脚本编程神器之awk语法案例详解

    AWK入门指南 文章目录 shell脚本编程神器之awk语法案例详解 安装AWK AWK 起步示例 AWK程序的结构 执行 awk 程序 awk 的错误提示 简单输出 打印每一行 打印特定行 NF,字 ...

  6. 秒懂的shell编程数组与冒泡算法排序详解

    shell编程数组与冒泡算法排序详解 一.数组的四种表达方式 二 .数组的增删改查操作 三 .数组传参 3.1 主程序向函数传数组参数 3.2 从函数返回数组到主程序 四 .冒泡算法排序 一.数组的四 ...

  7. c语言写扫雷新手详解

    c语言写扫雷新手详解 一.用到的知识点 1.分支语句 2.循环语句 3.二维数组 4.最好分块,使代码的功能更加独立,思维逻辑更加清楚,此程序我写了:test.c用来存放我的主函数,game.h用来定 ...

  8. 操作系统:基于页面置换算法的缓存原理详解(下)

    概述: 在上一篇<操作系统:基于页面置换算法的缓存原理详解(上)>中,我们主要阐述了FIFO.LRU和Clock页面置换算法.接着上一篇说到的,本文也有三个核心算法要讲解.分别是LFU(L ...

  9. php shell,php命令行写shell实例详解

    php 可以像java perl python 那样运行,今天发现如果我早早知道这个,或许我不会去学习java 和 python 当年学java不过为了一个程序放在服务器上,不停的跑啊跑,原来 php ...

最新文章

  1. java 读取excel_Java12POI操作Excel
  2. Python使用matplotlib可视化时间序列数据、并为时间序列曲线添加误差带、使用95%置信区间(Time Series Error Bands with confidence interval
  3. php 定义goto函数错误,goto语法在PHP中的使用教程
  4. 绝对养眼 Tech·Ed 2008大会SHOW GIRL动感热舞
  5. JavaScript的键盘事件
  6. 超详细的8psk调制解调通信系统讲解与仿真
  7. ZYNQ7000程序编译成功但烧写报错(使用Vitis2020.2)
  8. 在linux中dns不安装coching,ubuntu 8.04下openldap的安装和使用
  9. Reactor三种线程模型与Netty线程模型
  10. 数据绑定和第一个AngularJS 应用
  11. 可应用于实际的14个NLP突破性研究成果(一)
  12. 原子变量的原理与应用
  13. 通过调用外部exe的方法实现c#调用java
  14. HCIE-Security Day16:防火墙双机热备实验(四)防火墙直路部署,上行连接路由器(OSPF),下行连接交换机
  15. Arbor Networks Spectrum新安全平台发布:高级威胁平台通过内部企业网络连接互联网上的攻击...
  16. STM32驱动AD9833模块
  17. 设置clickhouse默认密码
  18. 处理行内元素行内块元素中间空隙
  19. java数字时钟代码,Android自定义数字时钟代码,android自定义时钟,package jp.t
  20. java界面添加按钮_java界面添加按钮

热门文章

  1. Bech32编码 (1)产生背景
  2. 石墨烯区块链(6)开发实例
  3. Hyperledger Fabric 核心模块(5)peerer共识
  4. C++ Primer 5th笔记(chap 14 重载运算和类型转换)类类型转换
  5. java渡劫期(32)----java进阶(ssm整合项目实战----房屋出租系统(渡劫失败))
  6. 6-PSCI Power Domain Tree Structure
  7. checked_delete问题: Beyond the C++ STL: an introduction to boostdeleter::do_it
  8. SocketAPI,CAsyncSocket,CSocket内幕及其用法
  9. VC6导入和使用二进制资源
  10. Windows_Reverse2逆向寒假生涯(25/100)