1、 前言 多線程
OO的複雜程度可能和寫完的時間成正比,多項式時週三電工實習不帶電腦,到了出租車已經發展成周三下午5點開始寫Readme……不過相比前幾回爲語法耗費時間,出租車更多的是爲設計來投入精力。多線程debug也頗有意思,在寫多部電梯時,本身猛然發現調試時加入的一句System.out.println居然會對結果產生影響(過後才發現原來是輸出調試信息到控制檯耗費了時間,致使輸出結束時其餘線程的狀態信息更新完成了,若不加輸出,其餘線程更新還沒有完成,發生錯誤),真的迷……app
2、 多線程電梯 函數
因爲三部電梯的出現,想要讓他們在三個線程中本身完成調度,就不得不在一個大請求隊列的基礎上,增長三個小的請求隊列,分別存儲三部電梯的請求。測試
每當InputHandler有了新的合法請求,就拿到大隊列的鎖,將其加入到大隊列中,叫醒調度器。一樣的,每當電梯跑完當前主請求,也會叫醒調度器。調度器被叫醒後,要把當前大隊列中的指令分給空電梯或能捎帶的電梯,直至InputHandler線程結束且四個隊列都爲空,程序結束。優化
此次雖然要求使用繼承,但因爲有捎帶的但電梯寫的並很差,索性直接重寫Scheduler類。值得一提的是時間的處理。但電梯因爲使用假時間,致使時間的計算冗餘重複。本次是使用系統時間,相應的就帶來了偏差,如因爲程序運行致使的每層樓實際並非3000ms,實際上會多出幾十毫秒。偏差的累計就致使了輸入較多時,開始出現明顯的時間偏差。所以我使用了消除偏差的方法,即在wait(3000)前,先計算所產生的偏差時間,將睡眠時間改成wait(3000-偏差),就使得時間徹底知足要求。spa
與以前的問題大體相同,圈複雜度和嵌套深度過大。出現問題的carry方法是用來判斷是否能夠捎帶,代碼以下。線程
1 /** 2 * 查找是否可被捎帶 3 * @param request 4 * @return 捎帶返回true,不然返回false 5 */ 6 public boolean carry(Request request) { 8 if (request.get_flag() == 2) {// ER 9 if (eleList[request.get_ele() - 1].get_status() == EleStatus.RUNNING) { 10 if (eleList[request.get_ele() - 1].get_dir() == EleDir.UP && request.get_dstfloor() > eleList[request.get_ele() - 1].get_curflo()) { 11 move(request, eleList[request.get_ele() - 1]); 13 return true; 14 } 15 else if (eleList[request.get_ele() - 1].get_dir() == EleDir.DOWN && request.get_dstfloor() < eleList[request.get_ele() - 1].get_curflo()) { 16 move(request, eleList[request.get_ele() - 1]); 17 return true; 18 } 19 } 20 return false; 21 } 22 else {// FR 24 int whichtocarry = 3, minsumdis = -1; 25 for (int j = 0; j < 3; j++) { 26 if (eleList[j].get_status() == EleStatus.RUNNING) { 28 if (eleList[j].get_dir() == EleDir.UP && request.get_dir() == 1 && request.get_dstfloor() > eleList[j].get_curflo() && request.get_dstfloor() <= eleList[j].get_dstflo()) {// bugwhere 29 if (minsumdis == -1 || eleList[j].get_sumdis() < minsumdis) { 30 minsumdis = eleList[j].get_sumdis(); 31 whichtocarry = j; 33 } 34 } 35 else if (eleList[j].get_dir() == EleDir.DOWN && request.get_dir() == 2 && request.get_dstfloor() < eleList[j].get_curflo() && request.get_dstfloor() >= eleList[j].get_dstflo()) { 36 if (minsumdis == -1 || eleList[j].get_sumdis() < minsumdis) { 37 minsumdis = eleList[j].get_sumdis(); 38 whichtocarry = j; 39 } 40 } 41 } 42 } 43 if (whichtocarry != 3) { 45 move(request, eleList[whichtocarry]); 46 return true; 47 } 48 return false; 49 } 50 }
很差的一點是沒用枚舉而是以變量來表示狀態,但因爲捎帶判斷條件比較複雜,可能也很難優化了。另外一個圈複雜度高的方法是opendoor方法,用來輸出,一樣的,由於加入了時間的偏差消除,使得方法更加複雜,代碼就不放了。debug
bug方面,本身沒有被報bug,可是測試的程序問題較多。如捎帶沒法判斷、時間計算錯誤等問題,多是他沒來得及調試致使的。設計
3、 IFTTT 3d
本次做業是一個IFTTT的文件掃描。因爲以前使用過相似的IFTTT類型app和堅果雲這樣的支持增量更新的雲盤,因此我對這個思想並不陌生。不過因爲本身的拖沓,致使了測試接口是在ddl前半小時寫的,徹底沒有調試,在被測時還有點擔憂。
本次因爲要掃描文件,而文件的位置(路徑)實際上包含在了文件的名字中,所以沒有用到樹來存儲,而是使用HashMap<String, long>來存儲,以文件的路徑做爲key,以其大小和最後修改時間做爲value,對目錄進行snopshot。以必定時間間隔更新snopshot,對監控文件進行查找和比對,若是觸發則進行任務,不然繼續掃描。
我採用的是每條指令一個線程。在更新snopshot後,每條指令開始對其監控文件進行查找。所以就出現了一個問題:多條指令監控一個文件,有recover、detail、summary操做時,若recover先完成,極可能致使後二者沒有被觸發而未記錄;recover後完成,極可能致使文件變化兩次都被detail和summary捕捉到,記錄了兩次。這個問題是因爲多線程的不肯定性致使的,所以不算作bug。但當互測時仍是被申報了,並且申報者說他解決這這個問題,我猜應該是將recover先不執行,保證必定觸發兩次。
能夠看到,Monitor類極可能成爲了一個god類,其中的snopshot方法和compareFile方法徹底能夠拿出來做爲新的類。度量分析也印證了這個猜測:
若是將這兩個方法拿出來,狀況會好不少。這是在設計上沒有想好就直接寫代碼的結果。compareFile方法複雜的緣由,很大程度上是因爲其中分了四種觸發器,拆成四個函數會大大下降複雜度。
此次做業因爲分類樹設置的很差,致使同源錯誤極可能被屢次掛樹,如個人modified判斷條條件畫蛇添足的判斷了大小,致使樹上的detail、summary、監控文件、監控目錄都掛了紅……最後經過申訴解決了。我測試的代碼一樣出現了這個問題。除此以外,我已經屢次經過讀手中最核心的運算部分的代碼,找到了難以測試的bug,也反映出了讀代碼的好處。
4、 出租車
出租車即便給了寫好的UI和map,也沒有幫助測試……因爲100輛車每200毫秒運動一次,很是難觀察其運動的正確與否,不少時候當看到GUI能正常運行,就基本判斷沒什麼bug了,所以讀代碼的重要性更加明顯。
本次我才用了100個出租車1個線程的假多線程,InputHandler線程輸入後,將請求發給Scheduler線程。Snopshot則將每200ms的位置快照發送給調度器。調度器根據位置搜索派單車輛,更新該車輛狀態,而TaxiList則每200毫秒sleep一次,再讓全部車移動,而後叫醒調度器進行調度。
能夠看到,許多方法出現了複雜度高的狀況。因爲車輛狀態較多,到知道move等方法的複雜。
我測試的代碼,出現了一個問題。經過讀他的代碼發現,他在判斷派單車輛時,將信用大於當前隊列最優的車加入隊列,此後從中選擇距離最小的一個,而忘記把信用較小的刪除。同時,個人代碼中也有個問題。快照對於每一個位置的車輛,要用arraylist來存儲,不然用int存其車輛編號,會致使同一位置有多輛車時,實際只存了1輛。
5、 心得體會
這三次多線程做業,讓我從徹底不會多線程的使用,到對鎖有了必定的理解,收穫很大。可是熬夜更加嚴重了emmm……每週三還要趕一天工才能將將完成。活着真好……