多線程的創建和啟動
點擊上方“IT那活兒”公眾號,關注后了解更多內容,不管IT什么活兒,干就完了!!!
- Java語言的JVM允許程序運行多個線程,多線程可以通過Java中的java.lang.Thread類來體現。
每個線程都是通過某個特定的Thread對象的run()方法來完成操作,經常把run()方法的主體作為線程體。 通過Thread方法的start()方法來啟動這個線程,而非直接調用run()。2. 多線程的創建
2.1 繼承Thread類
2.2 實現Runnable接口
- 實現類去實現Runnable接口中的抽象方法:run();
- 將此對象作為參數傳到Thread類的構造器中,創建Thread類的對象;
2.3 兩種創建方式比較
- Java中只允許單進程,以賣票程序TiketSales類來說,很有可能這個類本來就有父類,這樣一來就不可以繼承Thread類來完成多線程了,但是一個類可以實現多個接口,因此實現的方式沒有類的單繼承性的局限性,用實現Runnable接口的方式來完成多線程更加實用。
- 實現Runnable接口的方式天然具有共享數據的特性(不用static變量)。因為繼承Thread的實現方式,需要創建多個子類的對象來進行多線程,如果子類中有變量A,而不使用static約束變量的話,每個子類的對象都會有自己獨立的變量A,只有static約束A后,子類的對象才共享變量A。而實現Runnable接口的方式,只需要創建一個實現類的對象,要將這個對象傳入Thread類并創建多個Thread類的對象來完成多線程,而這多個Thread類對象實際上就是調用一個實現類對象而已。實現的方式更適合來處理多個線程有共享數據的情況。
- 聯系:Thread類中也實現了Runnable接口。
- 相同點:兩種方式都需要重寫run()方法,線程的執行邏輯都在run()方法中。
2.4 通過實現Callable接口
與Runnable相比,Callable功能更加強大:相比run()方法,可以有返回值;
- 需要借助FutureTask類,比如獲取返回結果。
2.5 通過線程池創建
經常創建和銷毀、使用量特別大的資源,比如并發情況下的線程,對性能影響很大。提前創建好多個線程,放入線程池中,使用時直接獲取,使用完放回線程池中。可以避免頻繁的創建銷毀,實現重復利用。3)優點
- 降低資源消耗(重復利用線程池中線程,不需要每次都創建);
- start():啟動當前線程, 調用當前線程的run()方法;
- run() : 通常需要重寫Thread類中的此方法, 將創建的線程要執行的操作聲明在此方法中;
- currentThread() : 靜態方法, 返回當前代碼執行的線程;
- join() : 在線程a中調用線程b的join(), 此時線程a進入阻塞狀態, 知道線程b完全執行完以后, 線程a才結束阻塞狀態;
- stop() : 已過時. 當執行此方法時,強制結束當前線程;
- sleep(long militime) : 讓線程睡眠指定的毫秒數,在指定時間內,線程是阻塞狀態;
1. CPU的調度策略
時間片:cpu正常情況下的調度策略。即CPU分配給各個程序的時間,每個線程被分配一個時間段,稱作它的時間片,即該進程允許運行的時間,使各個程序從表面上看是同時進行的。
如果在時間片結束時進程還在運行,則CPU將被剝奪并分配給另一個進程。如果進程在時間片結束前阻塞或結束,則CPU當即進行切換。而不會造成CPU資源浪費。在宏觀上:我們可以同時打開多個應用程序,每個程序并行不悖,同時運行。
在微觀上:由于只有一個CPU,一次只能處理程序要求的一部分,如何處理公平,一種方法就是引入時間片,每個程序輪流執行。
2. Java的調度算法
- 同優先級線程組成先進先出隊列(先到先服務),使用時間片策略。
1)線程的優先級等級(一共有10檔)
MAX_PRIORITY:10;
MIN_PRIORITY:1;
NORM_PRIORITY:5 (默認優先級)。
2)獲取和設置當前線程的優先級
說明:高優先級的線程要搶占低優先級線程cpu的執行權。但是只是從概率上講,高優先級的線程高概率的情況下被執行。并不意味著只有高優先級的線程執行完成以后,低優先級的線程才執行。
1. JDK中用Thread State類定義了線程的幾種狀態
- 新建:當一個Thread類或其子類的對象被聲明并創建時,新的線程對象處于新建狀態。
- 就緒:處于新建狀態的線程被start()后,將進入線程隊列等待CPU時間片,此時它已具備了運行的條件,只是沒分配到CPU資源。
- 運行:當就緒的線程被調度并獲得CPU資源時,便進入運行狀態,run()方法定義了線程的操作和功能。
- 阻塞:在某種特殊情況下,被認為掛起或執行輸入輸出操作時,讓出CPU并臨時中止自己的執行,進入阻塞狀態。
- 死亡:線程完成了它的全部工作或線程被提前強制性的中止或出現異常倒置導致結束。
1. 多線程的安全性問題解析
1.1 線程的安全問題
- 多個線程對賬本的共享, 會造成操作的不完整性, 會破壞數據;
當票數為1的時候,三個線程中有線程被阻塞沒有執行票數-1的操作,這是其它線程就會通過if語句的判斷,這樣一來就會造成多賣了一張票,出現錯票的情況。極端情況為,當票數為1時,三個線程同時判斷通過,進入阻塞,然后多執行兩側賣票操作。如果t1在輸出票號22和票數-1的操作之間被阻塞,這就導致這時候t1賣出了22號票,但是總票數沒有減少。在t1被阻塞期間,如果t2運行到輸出票號時,那么t2也會輸出和t1相同的票號22。通過以上兩種情況可以看出,線程的安全性問題時因為多個線程正在執行代碼的過程中,并且尚未完成的時候,其他線程參與進來執行代碼所導致的。2. 多線程安全性問題解決
當一個線程在操作共享數據的時候,其他線程不能參與進來。知道這個線程操作完共享數據的時候,其他線程才可以操作。即使當這個線程操作共享數據的時候發生了阻塞,依舊無法改變這種情況。在Java中,我們通過同步機制,來解決線程的安全問題。synchronized(同步監視器){需要被同步的代碼塊}
- 缺點:操作同步代碼時,只能有一個線程參與,與其他線程等待。相當于是一個單線程的過程,效率低。
將所要同步的代碼放到一個方法中,將方法聲明為synchronized同步方法。之后可以在run()方法中調用同步方法。要點:
- 同步方法仍然涉及到同步監視器,只是不需要我們顯示的聲明。
JDK5.0之后,可以通過實例化ReentrantLock對象,在所需要同步的語句前,調用ReentrantLock對象的lock()方法,實現同步鎖,在同步語句結束時,調用unlock()方法結束同步鎖。建議使用順序:Lock->同步代碼塊(已經進入了方法體,分配了相應的資源)->同步方法(在方法體之外)。2.3 線程同步的死鎖問題
1)原理
- 不同的線程分別占用對方需要的同步資源不放棄,都在等待對方放棄自己需要的同步資源,就形成了死鎖。
- 出現死鎖后,并不會出現異常,不會出現提示,只是所有的線程都處于阻塞狀態,無法繼續。
一個線程T1持有鎖L1并且申請獲得鎖L2,而另一個線程T2持有鎖L2并且申請獲得鎖L1,因為默認的鎖申請操作都是阻塞的,所以線程T1和T2永遠被阻塞了。導致了死鎖。這是最容易理解也是最簡單的死鎖的形式。但是實際環境中的死鎖往往比這個復雜的多。可能會有多個線程形成了一個死鎖的環路,比如:線程T1持有鎖L1并且申請獲得鎖L2,而線程T2持有鎖L2并且申請獲得鎖L3,而線程T3持有鎖L3并且申請獲得鎖L1,這樣導致了一個鎖依賴的環路:T1依賴T2的鎖L2,T2依賴T3的鎖L3,而T3依賴T1的鎖L1。從而導致了死鎖。線程在獲得一個鎖L1的情況下再去申請另外一個鎖L2,也就是鎖L1想要包含了鎖L2,也就是說在獲得了鎖L1,并且沒有釋放鎖L1的情況下,又去申請獲得鎖L2,這個是產生死鎖的最根本原因。另一個原因是默認的鎖申請操作是阻塞的。3)死鎖的解決辦法
很多情況下,盡管我們創建了多個線程,也會出現幾乎一個線程執行完所有操作的時候,這時候我們就需要讓線程間相互交流。
當一個線程執行完成其所應該執行的代碼后,手動讓這個線程進入阻塞狀態,這樣一來,接下來的操作只能由其他線程來操作。當其他線程執行的開始階段,再手動讓已經阻塞的線程停止阻塞,進入就緒狀態,雖說這時候阻塞的線程停止了阻塞,但是由于現在正在運行的線程拿著同步鎖,所以停止阻塞的線程也無法立馬執行。2. 所用到的方法
- wait():一旦執行此方法,當前線程就會進入阻塞,一旦執行wait()會釋放同步監視器。
- notify():一旦執行此方法,將會喚醒被wait的一個線程。如果有多個線程被wait,就喚醒優先度最高的。
- notifyAll() :一旦執行此方法,就會喚醒所有被wait的線程。
三個方法的調用者必須是同步代碼塊或同步方法中的同步監視器。 這三個方法并不時定義在Thread類中的,而是定義在Object類當中的。因為所有的對象都可以作為同步監視器,而這三個方法需要由同步監視器調用,所以任何一個類都要滿足,那么只能寫在Object類中。相同點:兩個方法一旦執行,都可以讓線程進入阻塞狀態。不同點:
- 兩個方法聲明的位置不同:Thread類中聲明sleep(),Object類中聲明wait()。
- 調用要求不同:sleep()可以在任何需要的場景下調用。wait()必須在同步代碼塊中調用。
- 關于是否釋放同步監視器:如果兩個方法都使用在同步代碼塊呵呵同步方法中,sleep不會釋放鎖,wait會釋放鎖。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/129430.html