1. 程式人生 > >漸入OO課的深處,探索多線程的秘密——OO第二次博客總結

漸入OO課的深處,探索多線程的秘密——OO第二次博客總結

知識 清晰 小技巧 無效 取出 ack 編程 結束 eight

一次又一次的挑戰,一次又一次全新的知識,我來到了多線程的面前


第五次作業

1、度量分析

技術分享圖片

>第五次作業由於很大程度上調用的是前兩次電梯的一些代碼,所以存在的問題與前幾次也十分相似。同時由於第一次使用多線程來解決問題,可能將某些功能過於集中的放在了個別類中。導致McCabe Cyclomatic Complexity以及Nested Block Depth出現標紅的現象。

2、類圖

技術分享圖片

>這次在類圖上面問題體現的也很明顯,在方法的分配上並沒有做的很平均。這主要是由於為第一次多線程作業,所以將大部分的功能全部賦予了elevator類中,導致類過於冗長而且類圖顯得特別難看。

3、關於BUG

  當我們在經歷了三次面向對象式的JAVA編程歷練以後,從最開始的一頭霧水到現在的略知一二,筆者以為自己終於已經適應了JAVA的語法與編寫規範,但第五次作業的降臨又讓我有了泰山壓頂般的感受。最後一次電梯作業,第一次運用多線程來處理實際問題,學長學姐口中OO最難的一次作業果然名不虛傳。直到周二晚上由於對多線程安全的理解不深刻,筆者的程序仍然存在些許小bug。由於精力實在有限,導致最後這一點bug被擱置了。~所幸這次測試的公測還是比較弱的,這次筆者的程序被公測和互測分別找了一個bug。公測的bug仍然在於對多線程的保護上,最終導致在多個電梯共同運行情況下運動量的計算出現了問題(3號電梯運動量被反復計算),導致公測的這一個點掛了。而測試者這次找到的問題同樣是由於多線程在運行上的不確定性導致,而這個問題則是反映在了捎帶這個功能上面,原本該捎帶的一次請求由於多線程完成度的問題導致在條件判斷上出現偏差,沒有被捎帶上。此外沒有被找到其他bug。而作為測試者,這次筆者拿到的程序問題還是不少的:首先在公測的格式測試方面,他對於錯誤格式的判斷很完善,缺在輸出格式上與要求有一點不同,然後是對於捎帶和同質的判斷也存在很大的問題,而且他把輸出時間輸出成了間隔時間,最終導致公測錯了很多。不過幸運的是對於最簡單的電梯和樓層請求,由於輸入時間為0,所以並沒有錯,也沒有達到一個無效的條件。而在公測之外,此人還有很多的細節方面也沒有做好。互測上筆者除了報告了他的輸出格式錯誤之外,還對單行20條請求和50行總請求進行了測試,他也沒有對此作出相應的錯誤響應。同時在一層輸出多條相同到達信息他也沒有處理。總之筆者認為他這次作業還是比較失敗的,很多並不難但比較細的東西沒有考慮到,而且輸出的錯誤是態度問題,筆者感覺他並沒有很認真的對待這次多線程作業。不過這次作業著實比較困難,周圍的同學也都多多少少出現了不同的問題,同樣這第一次的多線程之旅也給我們帶來了不少的反思與經驗。電梯的結束意味著新的開始,這次作業的bug就總結到這裏咯。


第六次作業

1、度量分析

技術分享圖片

>這一次作業由於主要是對文件進行一個操作,所以對於功能無論是從代碼量上還是代碼難度上都不是特別的難。但在判斷文件相對應的操作時需要判斷的條件過於繁多,過多的if-else所以造成了程序嵌套程度過深的情況,使Nested Block Depth出現標紅。而由於這些主要的判斷以及其他的主要功能部分都是在Trigger中完成的,又導致過多的方法集中在了一個類中,使McCabe Cyclomatic Complexity出現標紅現象。

2、類圖

技術分享圖片

>第六次作業的類圖還是比較清晰明確的,各個類分工也比較明確。可惜做的不好的一點在上面的度量分析與類圖中都可以反映出來,過多的功能性方法都集中在了Trigger這一個關於觸發器的類之中,使這一個類特別的突兀。

3、關於BUG

  學長們都說第五次和第六次作業是最難的兩次,事實也確實如此。IFTTT,當這個陌生的詞匯第一次出現在我眼簾的時候我根本不知道這次作業我們要做的究竟是什麽。而OO的魅力大概就在於每一次作業我們都需要自己去學習並探索新的知識,而這次正是對於文件的各種操作與監控。實際上這次作業無論是從代碼量還是邏輯難度上看都不高,但難就難在不清楚作業目標以及許多的新操作。不過有驚無險,這次作業的測試中筆者沒有被公測和互測找到bug,而筆者測試的程序寫的也十分完美。但可能是沒有及時收到issue上面的一些規則修改,沒有對於超過10個監控對象的處理。readme中也沒有響應的說明,筆者也只報了這樣一個bug。隨著作業的逐步深入,測試方面也變的越來越困難,所以筆者沒有時間也由於能力所限無法進行深層的測試,一次相對和平的作業就這樣過去了。


第七次作業

1、度量分析

技術分享圖片

>第一次關於出租車系列的作業,在功能上的描述與編寫上並不是很難。但想到並實施一個比較好的架構十分重要而且也不容易。我在深思熟慮之後才開始動工這次的作業,所以在類的分工做的十分的明確,但這個if-else的嵌套深度每一次作業都沒有解決。這可能是由於我經驗的上的不足,沒有很好的利用一些優質的算法與JDK自身函數,導致判斷次數與層次過於太多,以後這方面的問題我一定會多加的註意一些。

2、類圖

技術分享圖片

>由於是第一次出租車的作業,所以這次的類圖比較簡單,從名字上也能清楚的看出各個類的功能與屬性。

3、關於BUG

  在電梯之後又迎來了又一個系列性作業——出租車系列作業。萬事開頭難,雖然已經有了兩次多線程作業的經歷,但仍然對多線程的理解不夠清楚,在設計與實際工作過程中也不是說十分順利。由於是系列作業,關系到後面幾次作業的成敗,所以這次作業的bug修改也是極為重要。由於人工手動測試十分困難,所以公測只放了一些輸入格式上的一些錯誤,筆者的程序當然也沒有問題。當筆者看到在互測中自己被找到的bug是同質請求出現了沒有判斷的情況,仔細分析以後發現是雖然在兩次輸入上實際的時間差是小於100ms的,但由於筆者在對每條請求處理上有些復雜,導致程序運行時間偏長,使程序中兩次輸入的時間差超過了100ms,沒有判斷為同質請求,這樣是不符合實際的。所以筆者需要對這次代碼作出相應優化,或者利用假時間來判斷100ms,盡量減小程序運行時間導致的誤差。另一個bug則是測試者偶然發現當輸入滿足一定的條件時我的出租車信用度在計算上會出現差錯,雖然現在仍然不知道具體問題是什麽,但可以肯定的是bug出現在信用度計算上的條件與位置一塊。這就是筆者這次作業被找到的bug。相同筆者測試的程序也存在一些類似的問題:首先筆者的測試任務代碼在公測方面沒有判斷出發點和目的點相同的情況,也就是沒有忽略此類請求,這也是他公測唯一的錯誤。在互測上,首先比較明顯的一個問題是當輸入請求多於一條時,對方程序的出租車雖然會對所有請求有所響應,但每次都是在接到單之後卻不去運動,筆者猜測是他在狀態轉化上出現了問題,導致出租車出現了不繼續跑的情況。對方的第二個bug情況就和筆者的bug類似了,由於我們都是直接使用系統時間(也就是真時間)來進行各種判斷與輸出,所以每次模擬出租車運行一格200ms實際程序運行超過200ms,所以在輸出上會有所體現。因為結果精確到100ms,所以在一定量累積以後這個誤差量達到100ms使輸出出現問題。而這個由於程序運行時間導致的誤差錯誤筆者在上面自己的bug處也有所提及。

  至此,這三次作業的bug分析之旅也就結束了。


心得體會

1.當步入多線程的世界,才知道這裏面的路是多麽的艱難。這幾次作業我覺的最困難的點大致有兩個,首先就是針對於一個項目的架構設計。由於是多線程的設計,所以在每次動工之前需要想清楚要建立多少個類,同時需要想多少個線程才能把這個功能做到最佳化。
2.其二就是關於多線程最重要的一環:線程安全問題。在完成代碼基礎功能之後,如果對線程不加以管控,往往會出現很多不合乎常理的錯誤,這都是因為多個線程同時運行情況下對公共資源的無管控的爭奪,再加上多線程運行的不確定性導致公共資源出現意料之外的錯誤。所以在功能完成後要對幾個線程之間的聯系作出分析,比如幾乎每次作業都用到的請求隊列在輸入線程和調度器線程之間的共用,請求的存取就好比消費者與生產者之間的經典案例。需要對之間的紐帶——請求隊列進行上鎖操作,而這就要對所有的共用資源全部分析清楚。當然在一定的實踐與理解之後,筆者也意識到並不是所有涉及到共用資源都需要上鎖,有的隊伍共用資源的調用只是拿到其性質或者其他的東西,並不對其進行操作與改變,這一部分的共用其實是不需要加鎖保護的。就針對於請求隊列而言,重要的是對存入請求以及取出請求(刪除請求)進行保護,否則會出現亂了套的情況。
3.對於多線程代碼的調試也是比較有感觸的一個部分。它本身的不確定性很大程度上就決定了多線程的調試並不能依靠加斷點來單步調試。所以需要靈活而巧妙地運用輸出調試來找到問題的所在。而且在每個線程的開始去進行輸出標誌也是屢試不爽的一個小技巧。同時多線程也有許多的好處,比如多線程可以把任務分塊執行,分塊後可以同時進行而不用等待。這樣效率更高如下載文件,通過多線程就可以實現多文件下載。同時線程安全的實現方式也是有很多種的而我使用的Synchronized關鍵字:編譯後會在同步塊前後分別形monitorenter和monitorexit這兩個字節碼指令。這兩個指令都需要一個引用類型的參數來指明要鎖定和解鎖的對象。如果沒有明確指定對象參數,那就根據synchronized修飾的是實例方法還是類方法,去取對應的對象實例或Class對象來作為鎖對象。在執行monitorenter指令時,首先嘗試獲取對象的鎖,如果沒有被鎖定或者當前線程已經擁有了該對象的鎖,則將鎖計數器加1,相應的執行moniterexit時,將鎖計數器減1,當計數器為0時,鎖就被釋放了。如果獲取對象鎖失敗,則當前線程就要阻塞等待。


寫在最後

  ~~至此,課程已經過去了一多半,我相信大家都已經對OO和JAVA有了一定程度的熟悉與個人的感受吧。在這裏再次對所有在完成作業過程中幫助過我的同學以及認真負責測試我代碼的同學表示衷心的感謝。希望大家能堅持下去,一起加把勁,努力去迎接新的挑戰與勝利的曙光。

漸入OO課的深處,探索多線程的秘密——OO第二次博客總結