之前作者做的那個實驗有誤,希望大家見諒,在室友的質疑之後覺得的確存在着很大的問題,所以自己今天又把一些邏輯上的漏洞又重新完善了一下。其實主要的邏輯漏洞又兩個方面
第一就是,最後沒有在總時間內到達的線程,最終是不能直接按照到達時間排序完之後的順序直接打印的,而應該是每一次,將一個輪迴裏面能夠到達的線程按照那樣的檢查機構打印出來後,剩下的沒有到達的線程也應該是重新打入那個隊列,然後在按照之前的規則繼續打印,直到最後的隊列中再也不存在元素,說明所有的線程都已經執行完畢
第二個就是之前的一個總時間計算問題,作者之後發現有一部分的總時間計算是存在問題的,下面作者會通過一個圖來讓讀者們理解:
這樣所有情況的圖基本上都完成了。作者之前的第一版本,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
}
}
}
最後作者提供了兩個用例測試了兩個版本的答案,最後的確證明邏輯上是存在漏洞的,下面分別是兩個代碼執行的結果圖
首先是讀者優先
正確的答案:
錯誤的答案演示:
之後是寫者優先的演示
正確的答案演示:
錯誤的答案演示:
作者很菜,如果還有錯誤,還望大家指正!!!