java線程與模式總結_第1頁
java線程與模式總結_第2頁
java線程與模式總結_第3頁
java線程與模式總結_第4頁
java線程與模式總結_第5頁
已閱讀5頁,還剩5頁未讀 繼續免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

1、線程是Java的一大特色,從語言上直接支持線程,線程對于進程來講的優勢在于創建的代價很小,上下文切換迅速,當然其他的優勢還有很多,缺點也是有的,比如說對于開發人員來講要求比較高,不容易操作,但是Java的線程的操作已經簡化了很多,是一個比較成熟的模型。很多時候,我們都用不到線程,但是當我們有一天不走運(或者走運)的時候,我們必須要面對這個問題的時候,應該怎么辦呢?本文是我的學習筆記和一些總結,試圖解決這個問題,引領還沒有接觸過Java 線程的開發人員進入一個Java線程的世界,其實很多東西在網路上已經有朋友總結過了,不過我感覺沒有比較循序漸進,要么太基礎,要么太高深,所以這邊我由淺到深的總結一

2、下。但是很顯然,我的資歷尚淺,能力也很有限,如果有什么錯誤還望不吝賜教!麻煩發送mail到: 而且,這些大部份的都有源碼,如果需要也可以發mail到這個郵箱,真的非常希望有人能指正我的錯誤!(一) 基本的API介紹1.   如何創建一個可以執行的線程類       創建一個線程有兩個辦法:繼承Thread類或者實現Runnable接口。       首先:繼承Thread類       這里一般只

3、需要我們來重寫run這個方法。下面是代碼:public class SimpleThread extends Thread        public SimpleThread()               start();              Override 

4、0;  public void run()               while (true)                      System.out.println(this);      

5、;               / Imply other thread can run now, but we cannot assume that it will                     / work well every time, ac

6、tually , most of time we can get the same                     / result, but not to a certainty.                 

7、60;   / yield();                     try                          

8、;   sleep(100);                     catch (InterruptedException e)                     

9、60;       e.printStackTrace();                                          其次:

10、實現Runnable接口,代碼如下:       Public class Inner implements Runnable               private Thread thread;              public Inner(String name)

11、                     thread = new Thread(this, name);                     thread.start();  

12、;                          public void run()                      while (true) &

13、#160;                    try                              &#

14、160;     Thread.sleep(10);                            catch (InterruptedException e)           

15、                         throw new RuntimeException(e);                     

16、;                                          2.   幾個常用的API這邊介紹幾個常見而且重要的的線程API,這邊JDK文檔有更加詳細的說明,其實J

17、DK的文檔就是個很好的學習資料,常備很重要哦!方法說明start使線程開始執行,實際上這個方法會調用下面的run這個方法,如果這個線程已經開始執行,則會扔出IllegalThreadStateExceptionsleep是當前已經運行的線程休眠一段時間。如果當前線程已經被別的線程中斷的話,將會扔出InterruptedException,而且interrupted標志也會被清空。這個方法有兩個版本,具體參看JDK文檔。run線程執行的業務邏輯應該在這里實現。join等待另外一個線程死亡。如果當前線程已經被別的線程中斷的話,將會扔出InterruptedException,而且interrupt

18、ed標志也會被清空。yield使當前線程臨時的中斷執行,來允許其他線程可以執行,因為Java的線程模型實際上映射到操作系統的線程模型,所以對于不同的操作系統,這個方法的就有不同的意義。對于非搶占式Operating System,這個方法使得其他線程得到運行的機會,但是對于搶占式的OS,這個方法沒有太多的意義。關于這個方法,后邊還有更多的介紹。waitWait方法和后邊的兩個方法都來自Object。看過Java源碼的可以知道,這三個方法都是Native方法,使比較直接的和操作系統打交道的方法。這個方法的作用是讓當前線程等待,直到被喚醒或者等待的時間結束。當前線程進入等待隊列的時候,會放棄當前所

19、有的資源,所以當前線程必須獲得這些對象的Monitor,否則會扔出IllegalMonitorStateException 關于wait方法的更多,后邊會有介紹到。notify通知其他線程可以使用資源了。這個方法的使用要求很多,總之需要當前線程獲得被調用的notify方法的對象的monitor。比如:                         

20、;                        synchronized (person)                        

21、                                 person.notify();               &

22、#160;                                 其實,獲得monitor的方法還有別的,這里簡單介紹一下:1.         執行這個對象的一個同步的方法2. 

23、        執行這個對象的同步塊3.         執行一個同步的靜態方法notifyAll除了通知所有的線程可以準備執行之外,跟上面的方法要求一樣。但是只有一個線程會被選擇然后執行,這個就跟優先級和其他狀態有關系了。interrupt中斷線程。這邊只是介紹了幾個常用的API,但是非常重要,其他的API可以查看JDK的相關文檔。但是在操作系統的概念中,很顯然,對于一個線程應該還有別的狀態,對,確實還有,但是Java在實現的映射的時候,也實現

24、了這些方法,只是不贊成使用,下面的主題將討論這些方法以及這些方法的替代方法。3.   已經不贊成使用的方法對于一些不應該再使用的東西,有時候被稱為反模式antipattern。這些都是概念上的東西,對于我們開發人員來講,需要做的就是寫出好的代碼。方法說明stop強制使當前的線程停止執行。實際上,作為開發人員我們會意識到,線程的復雜程度是沒有邊際的,而這個方法這樣武斷的停止一個線程,必然導致問題產生。也就是說,這個方法天生就有問題。比如說一個線程掌握了很多對象,并且改變了其中一些的狀態,如果突然當前對象突然被停止,將會釋放這些對象的monitor,這個時候被改變狀態的對象就是

25、被損壞的對象,其他線程再來操作的時候問題就出來了。替代的辦法就是讓當前線程正常結束,不使用這個方法。就是我們設置一些標志,如果這些標志滿足的時候,我們結束線程。下面用JDK的例子:    private Thread blinker;    public void start()         blinker = new Thread(this);        blinker.start();

26、        public void stop()         blinker.stop();  / UNSAFE!        public void run()         Thread thisThread = Thread.currentThread();    

27、60;   while (true)             try                 thisThread.sleep(interval);            catch (Interrup

28、tedException e)                        /do something        修改后:    private volatile Thread blinker;    public void stop() &#

29、160;       blinker = null;        public void run()         Thread thisThread = Thread.currentThread();        /Check the flag        w

30、hile (blinker = thisThread)             try                 thisThread.sleep(interval);            catch (Interrupt

31、edException e)                        /do something        當然如果這個方法中間有wait方法的調用的話,而且正在等待,我們可以使用這個辦法來結束:Thread.currentThread().interrupt();然后處理InterruptedEx

32、ception 這個我也實現了避免使用stop方法的一個類,在源碼中可以看到。suspend這個方法天生就有導致死鎖的可能。如果當前線程持有很多對象的鎖,但是當他suspend的時候,這些鎖是不會釋放的,想想就知道應該什么可能會發生,所以這個方法應該盡量不用。這里我們有辦法替代這個方法,其實根替代stop的方法差不多,就是用wait方法來實現掛起,而不是事實上的掛起。比如:Override    SuppressWarnings("static-access")      &

33、#160;                     public void run()                           

34、         while (true)                                        

35、0;  try                                                 

36、; Thread.currentThread().sleep(1000);                                            

37、;     / Double check                                           &#

38、160;     if (isSuspended)                                           &#

39、160;             synchronized (this)                                   

40、;                             while (isSuspended)                   &#

41、160;                                                 &#

42、160; wait();                                                

43、                                                   

44、;                                                  

45、0;                                                  

46、60;         catch (InterruptedException e)                                     &

47、#160;            / null                                     

48、                                                   

49、0;                     這樣做可以同樣實現掛起,但是仍然會釋放資源。resume很顯然,這個方法和上面的方法是對應的,所以上面用了wait方法來替代,所以這邊應該用notify這個方法或者notifyAll這個方法來替代。 其實這邊可以把上面的實現方式結合起來,實現一個可以安全stop和suspend的線程。這個在我的源碼里有實現,但是不知道是不是對的。不過感覺原則應該沒有問

50、題,那就是設置標志來結束或者掛起線程,而不是使用這些過時的方法。4.   跟線程相關的關鍵字跟線程相關的關鍵字我能夠想到的就下面兩個:關鍵字說明volatile這個關鍵字告訴編譯器不要對這個屬性或者值進行優化,也就是為了保證這個變量的同步性,如果這個值被更新,其他線程應該可以馬上訪問到最新的值,而不是“臟值”。其實這個關鍵字是同步互斥的一個模型,但是現在沒有實現。synchronized給對象或者Class加鎖,分為對象鎖和Class鎖。對象鎖只是加在當前對象上,對別的對象沒有影響,這種加鎖一般都是把這個關鍵字用在方法和同步塊上面。Class鎖就是加在這個Class上面,所

51、有的其他對象想訪問這個Class的對象的任何同步方法,必須獲得這個鎖。這種鎖一般把這個關鍵字用在靜態方法中,或者顯示的這樣實現:                                   synchronized (AClass.class) 

52、60;                                         while (isSuspended)       

53、                                           wait();      

54、0;                                                  

55、60;                   一般我們很少用Class鎖。 這里簡單提一下monitor,個人感覺這里把monitor認為是一把鎖也可以。網絡上有朋友解釋的比較好:在java中,每個對象只有一個相應的monitor,一個mutex,而每一個monitor都可以有多個“doors”可以進入,即,同一個monitor中被守護的代碼可以在不同的地方,因為同一個對象可以出現在不同的代碼段,只要mute

56、x鎖定的對象是同一個,每個入口都用Synchronized關鍵字表明,當一個線程通過了Synchronized關鍵字,它就所住了該monitor所有的doors其實線程的使用不在于語言的API,而在于對操作系統的理解和一些常見的調度算法,其實個人理解經驗比較重要,后邊介紹到線程的實現模式和設計模式。其實我還是以前的想法:對于語言的學習,首先學習語法和API,然后學習如何使用這些API在語法的框架內編寫出高效的程序。很顯然,模式就是實現后邊的重要方法。模式常見的分類有實現模式、設計模式和架構模式。這里限于本人的能力問題,沒有理解到架構上面去,所以這里只是研究了前兩個。(二) 線程實現模式實現模式

57、這邊主要參考自Effective Java這本書,至少分類是,但是很多內容應該會很不相同,當然還有Think in java。Effective Java是短小精悍的一本書,其中有太多的Java的關于實現模式的建議,但是這邊把這本書的內容歸類到實現模式,是我個人的想法,如果有什么不正確,萬望指正。但是,個人認為這些概念性的東西仍然不會損害到我們需要討論的問題的實質。1.         共享數據同步上面有提到過synchronized關鍵字,這個關鍵字保證一段代碼同時只能有一個線程在執行,保證別人不會看到對象處于不

58、一致的狀態中。對象將從一種一致的狀態轉變到另一種一致的狀態,后來的線程將會看到后一種狀態。在Java中,虛擬機保證原語類型(除double和long)的讀寫都是原子性的。即不需要同步,但是如果不對這樣的數據讀寫進行同步,那么后果將很嚴重。可以參照Effective Java的解釋,這里還要簡單的提示意下,Effective Java中有提到double check這種方式,而且我的源代碼中多次用到這種方法,單是需要提醒一下,如果用這種方式來實現singleton的話,就不可以了,因為這樣有可能導致不完整的對象被使用,單是源碼中的double check用的都是原語類型,所以OK。 這邊的建議是

59、如果修改原語類型或者非可變類的屬性,可以同步或者使用volatile關鍵字。如果是其他對象,必須同步。關于盡量少使用同步,這邊的建議是,我們這樣的初學者在不知道如何優化的情況下就不要優化,我們要的是正確的程序,而不是快的程序。2.         wait方法的使用wait方法是一個很重要的方法,前面有介紹過這個方法,不但可以使一個線程等待,而且可以作為實現suspend的替代方法的一個方法。Wait方法的標準使用方式如下:       

60、0;                           synchronized (obj)                     &#

61、160;                     while (condition)                          &#

62、160;                      wait();                           

63、        這里,對應wait方法還有一個notify和notifyAll方法,到底我們應該如何使用這兩個方法來喚醒等待的線程呢?很顯然notifyAll的使用是最安全的,但是會帶來性能的降低。這里又提到我們初學者,應該優先考慮這個方法,而不是notify。3.         不要依賴線程調度器,管好自己的事情Thread.yield這個方法并不能保證線程的公平運行,所以這個方法不應該依賴。還有就是線程的優先級,Java的線程優先級有10個

64、等級,但是這個等級幾乎沒有什么用處,所以我們也不應該依賴這個優先級來控制程序,當然仍然可以優化一些服務,但是不能保證這些服務一定被優化了。我們應該盡量控制對critical resources的方法線程數,而不是用優先級或者yield來實現對資源的訪問。4.         不要使用線程組線程組是一個過時的API,所以不建議使用。但是也不是一無是處,“存在即合理”嘛!(三) 線程設計模式什么是模式呢?Martin Flower先生這樣描述:一個模式,就是在實際的上下文中,并且在其他上下文中也會有用的想法。這邊的線程

65、設計模式大部分參考自林信良先生的設計模式,還有一些網路的文章,這些都是前輩們在使用線程時候的經驗,非常值得我們借鑒。還有就是林信良先生的設計模式非常通俗易懂,是入門級選手的最佳選擇。關于線程的模式應該還有別的,只是我這邊現在只能總結這么多了,能力有限。這邊用大量的UML來描述這些模式,但是由于我的UML學的不好,而且工具用的不怎么熟,畫的圖應該會有些問題,當做草圖來看就好了。1.         Single Threaded Execution這個模式在Java里說的話有點多余,但是這邊還是先拿這個開胃一下。很明

66、顯,從字面的意思,就是說同一時刻只有一個線程在執行,Java里用synchronized這個關鍵字來實現這個模式。確實多余 L!看看UML吧!其實用這個圖來描述有點不好。其實應該用別的圖來描述會比較好!比如協作圖。2.         Guarded Suspension網上有一個比較好的描述方式:要等我準備好噢!這里我們假設一種情況:一個服務器用一個緩沖區來保存來自客戶端的請求,服務器端從緩沖區取得請求,如果緩沖區沒有請求,服務器端線程等待,直到被通知有請求了,而客戶端負責發送請求。很顯然,我們需要對緩沖區進行保

67、護,使得同一時刻只能有一個服務器線程在取得request,也只能同一時刻有一個客戶端線程寫入服務。用UML描述如下:具體實現可以參看代碼。但是,這個模式有一點點瑕疵,那就是緩沖區沒有限制,對于有的情況就不會合適,比如說您的緩沖區所能占用的空間受到限制。下面的Producer Consumer Pattern應該會有所幫助。3.         Producer ConsumerProducer Consumer跟上面的Guarded Suspension很像,唯一的區別在于緩沖區,Guarded Suspensio

68、n模式的緩沖區沒有限制,所以,他們適用的場合也就不一樣了,很顯然,這個考慮應該基于內存是否允許。Producer Consumer的緩沖區就像一個盒子,如果裝滿了,就不能再裝東西,而等待有人拿走一些,讓后才能繼續放東西,這是個形象的描述。可以參考下面的UML,然后具體可以參看源碼。4.         Worker ThreadWorker Thread與上面的Producer-consumer模式的區別在于Producer-consumer只是專注于生產與消費,至于如何消費則不管理。其實Worker Thread

69、模式是Producer-consumer與Command模式的結合。這邊簡單描述一下Command pattern。用UML就和衣很清晰的描述Command pattern。這個模式在我們的很多MVC框架中幾乎都會用到,以后我也想寫一個關于Web應用的總結,會提到具體的應用。其實Command pattern模式的核心就是針對接口編程,然后存儲命令,根據客戶短的請求取得相應的命令,然后執行,這個跟我們的Web請求實在是太像了,其實Struts就是這樣做的,容器相當于Client,然后控制器Servlet相當于Invoker,Action相當于ICommand,那么Receiver相當于封裝在A

70、ction中的對象了,比如Request等等。上面描述過Command pattern之后,我們回到Worker模式。這邊看看worker的UML:從圖中可以看到,CommandBuffer這個緩沖區不僅僅能夠存儲命令,而且可以控制消費者WorkerThread。這就是Worker模式。下面的Sequence應該會更加明確的描述這個模式,具體可以參看代碼。5.         Thread-Per-MessageThread-Per-Message模式是一個比較常用的模式了,如果我們有一個程序需要打開一個很大的文件

71、,打開這個文件需要很長的時間,那么我們就可以設計讓一個線程來一行一行的讀入文件,而不是一次性的全部打開,這樣從外部看起來就不會有停頓的感覺。這個模式Future模式一起學習。6.         Read-Write-Lock考慮這樣一種情況:有一個文件,有很多線程對他進行讀寫,當有線程在讀的時候,不允許寫,這樣是為了保證文件的一致性。當然可以很多線程一起讀,這個沒有問題。如果有線程在寫,其他線程不允許讀寫。如果要比較好的處理這種情況,我們可以考慮使用Read-Write-Lock模式。這個模式可以如下描述:其實這

72、個模式的關鍵在于鎖實現,這里有個簡單的實現如下:public class Lock        private volatile int readingReaders = 0;        SuppressWarnings("unused")       private volatile int writingWriters = 0;    &

73、#160;   SuppressWarnings("unused")       private volatile int waitingWriters = 0;        public synchronized void lockRead()                try &

74、#160;                    while (writingWriters > 0 | waitingWriters > 0)                      

75、       wait();                                   catch (InterruptedException e)     &

76、#160;                / null                             readingReaders+;   

77、            public synchronized void unlockRead()               readingReaders-;              notifyAll();  &#

78、160;            public synchronized void lockWrite()                waitingWriters+;              try   &

79、#160;                  while (writingWriters > 0 | readingReaders > 0)                        

80、     wait();                                   catch (InterruptedException e)       &

81、#160;              / null              finally                      wa

82、itingWriters-;                             writingWriters+;               public synchronized void u

83、nlockWrite()               writingWriters-;              notifyAll();       其實在鎖里還可以添加優先級之類的控制。 7.      &

84、#160;  FutureFuture模式是Proxy模式和Thread-Per-Message模式的結合。考慮下面的情況:比如我們的word文檔,里頭有很多圖片在末尾,我們打開這個文檔的時候會需要同時讀取這些圖片文件,但是很明顯,如果剛剛開始就全部讀取進來的話會消耗太多的內存和時間,使得顯示出現停頓的現象。那么我們應該怎么做呢,我們可以做這樣一個對象,這個對象代表需要讀入的圖片,把這個對象放在圖片的位置上,當需要顯示這個圖片的時候,我們才真正的填充這個對象。這個就是Proxy模式了。當然Proxy不僅僅是這么個意思,Proxy的真正意思是我們之需要訪問Proxy來操作我們真正需要操作的對象,以便實現對客戶段的控制。這邊先簡單描述一下Proxy模式:當Client請求的時候,我們用Proxy代替RealObject載入,當Client真正需要getObject的時候,Proxy將調用Rea

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論