建立執行緒兩種方式的比較
通過鐵路售票程式來理解實現多執行緒的兩種方法:通過java.lang.Thread類和通過Runnable介面
java中有兩種實現多執行緒的方式。一是直接繼承Thread類,二是實現Runnable介面。那麼這兩種實現多執行緒的方式在應用上有什麼區別呢?
為了回答這個問題,我們可以通過編寫一段程式碼來進行分析。我們用程式碼來模擬鐵路售票系統,實現通過四個售票點發售某日某次列車的100張車票,一個售票點用一個執行緒表示。
我們首先這樣編寫這個程式:
上面的程式碼中,我們用ThreadTest類模擬售票處的售票過程,run方法中的每一次迴圈都將總票數減1,模擬賣出一張車票,同時該車票號打印出來,直接剩餘的票數到零為止。在ThreadDemo1類的main方法中,我們建立了一個執行緒物件,並重復啟動四次,希望通過這種方式產生四個執行緒。從執行的結果來看我們發現其實只有一個執行緒在執行,這個結果告訴我們:一個執行緒物件只能啟動一個執行緒,無論你呼叫多少遍start()方法,結果只有一個執行緒。
我們接著修改ThreadDemo1,在main方法中建立四個Thread物件:
這下達到目的了嗎?
從結果上看每個票號都被列印了四次,即四個執行緒各自賣各自的100張票,而不去賣共同的100張票。這種情況是怎麼造成的呢?我們需要的是,多個執行緒去處理同一個資源,一個資源只能對應一個物件,在上面的程式中,我們建立了四個ThreadTest物件,就等於建立了四個資源,每個資源都有100張票,每個執行緒都在獨自處理各自的資源。
經過這些實驗和分析,可以總結出,要實現這個鐵路售票程式,我們只能建立一個資源物件,但要建立多個執行緒去處理同一個資源物件,並且每個執行緒上所執行的是相同的程式程式碼。在回顧一下使用介面編寫多執行緒的過程。
上面的程式中,建立了四個執行緒,每個執行緒呼叫的是同一個ThreadTest物件中的run()方法,訪問的是同一個物件中的變數(tickets)的例項,這個程式滿足了我們的需求。在Windows上可以啟動多個記事本程式一樣,也就是多個程序使用同一個記事本程式程式碼。
可見,實現Runnable介面相對於繼承Thread類來說,有如下顯著的好處:
(1)適合多個相同程式程式碼的執行緒去處理同一資源的情況,把虛擬CPU(執行緒)同程式的程式碼,資料有效的分離,較好地體現了面向物件的設計思想。
(2)可以避免由於Java的單繼承特性帶來的侷限。我們經常碰到這樣一種情況,即當我們要將已經繼承了某一個類的子類放入多執行緒中,由於一個類不能同時有兩個父類,所以不能用繼承Thread類的方式,那麼,這個類就只能採用實現Runnable介面的方式了。
(3)有利於程式的健壯性,程式碼能夠被多個執行緒共享,程式碼與資料是獨立的。當多個執行緒的執行程式碼來自同一個類的例項時,即稱它們共享相同的程式碼。多個執行緒操作相同的資料,與它們的程式碼無關。當共享訪問相同的物件時,即它們共享相同的資料。當執行緒被構造時,需要的程式碼和資料通過一個物件作為建構函式實參傳遞進去,這個物件就是一個實現了Runnable介面的類的例項。