之前作者做的那个实验有误,希望大家见谅,在室友的质疑之后觉得的确存在着很大的问题,所以自己今天又把一些逻辑上的漏洞又重新完善了一下。其实主要的逻辑漏洞又两个方面
第一就是,最后没有在总时间内到达的线程,最终是不能直接按照到达时间排序完之后的顺序直接打印的,而应该是每一次,将一个轮回里面能够到达的线程按照那样的检查机构打印出来后,剩下的没有到达的线程也应该是重新打入那个队列,然后在按照之前的规则继续打印,直到最后的队列中再也不存在元素,说明所有的线程都已经执行完毕
第二个就是之前的一个总时间计算问题,作者之后发现有一部分的总时间计算是存在问题的,下面作者会通过一个图来让读者们理解:
这样所有情况的图基本上都完成了。作者之前的第一版本,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 } } }
最后作者提供了两个用例测试了两个版本的答案,最后的确证明逻辑上是存在漏洞的,下面分别是两个代码执行的结果图
首先是读者优先
正确的答案:
错误的答案演示:
之后是写者优先的演示
正确的答案演示:
错误的答案演示:
作者很菜,如果还有错误,还望大家指正!!!