1.java多線程技能
1.1.進程和多線程的概念及線程的優勢
進程:進程是操做系統的基礎,是一次程序的執行,是一個程序及數據在處理機上順序執行時所發生的活動。是程序在一個數據集合上運行的過程,他是系統進行資源分配和調度的一個獨立單位。
線程:進程中獨立運行的子任務。
多線程優勢:最大限度的利用CPU的空閒時間來處理其餘子任務。
1.2. 使用多線程
直接使用java main方法運行程序,也運行了一個線程。一個進程至少有一個線程在運行。
實現多線程的方式主要有兩種: 繼承Thread類,實現Runnable接口。其實Thread 也是實現了Runnable接口,二者沒有本質的區別。
直接啓動run方法,沒有異步效果,即便起了不少線程也會是單線程執行的模式,指向start方法纔會異步執行,將線程對象交給線程規劃器執行。
執行start的順序,不表明線程啓動的順序,start方法只是通知線程調度器此線程已經準備就緒,具體順序由jvm控制。
爲了能夠繼承其餘的類,通常採用實現Runnable接口的方式編寫多線程代碼。
非線程安全:多個線程對同一個對象中的同一個實例變量進行操做時,會出現值被更改,值被同步的狀況,進而影響程序的指向流程
jvm中 i - - 分三步執行,取得i值,計算i-1,對i進行復制。多個線程同時訪問,就會出現問題
1.3. currentThread()方法
返回代碼段正在被哪一個線程調用的信息。
1.4. isAlive()方法
判斷當前線程是否處於活動狀態
線程處於正在運行,或者準備開始運行的階段,就認爲線程是存活測狀態。
currentThread 表示當前執行的線程,this表示當前的對象
1.5.sleep()方法
在指定的毫秒數內讓當前正在執行的線程 休眠。
1.6. getId()方法
取得線程的惟一標識
1.7. 中止線程
使用退出標誌,使線程征程退出,run執行完以後退出
使用stop強行中止線程, 不推薦這麼使用,stop,suspend及resume都是做廢過時的方法
使用interrupt方法中斷線程,interrupt只是給當前線程打了一箇中止標誌,並非真正的中止線程。
this.interrupted()測試當前線程是否已是中斷狀態,執行後具備將狀態標誌清除爲false的功能。
this.isInterrupted()測試線程對象是否已是中斷狀態,不清除狀態標誌。
線程中拋出異常也會中止線程。
在sleep狀態進入執行interrupt,會拋出,InterruptedException異常,反過來也是同樣的
可使用interrupt和return結合來中止線程,不過建議使用拋異常的方式來實現線程的中止。異常能夠上拋,使異常事件得以傳播。
1.8. 暫停線程
suspend和resume方法分別是暫停和恢復線程的運行,可是極易形成,公共的同步對象獨佔,其餘線程沒法訪問到這個對象,問題不少,建議不適用。
1.9 yield()方法
放棄當前的CPU資源。可是本身會進入就緒隊列,因此可能放棄以後又立刻佔有CPU資源
1.10. 線程的優先級
優先級越高的線程,就越容易獲得CPU資源,可是具體由誰來執行仍是由線程規劃器來肯定執行的線程。使用setPrisetPriority()方法設置優先級,級別爲1-10
線程的優先級具備繼承性,執行線程默認和啓動線程具備相同的優先級
線程的優先級和代碼的執行順序無關,可是CPU儘可能將執行資源讓給優先級比較高的線程。
線程優先級的特性:繼承性,規則性和隨機性
1.11. 守護線程
線程有兩種:用戶線程和守護線程
守護線程是一種特殊的線程,沒有非守護線程時,守護線程會自動銷燬。
垃圾回收機制就是典型的守護線程,使用setDaemon(true)就能夠將線程設置爲守護線程。
2. 對象及變量的併發訪問
2.1. synchronized同步方法
方法內部的變量永遠是安全的,由於做用域就在方法內
兩個線程訪問同一個對象的同步方法時,必定是線程安全的。
關鍵字synchronized取得的鎖都是對象鎖,而不是將一段代碼或者方法當作鎖
只有共享資源的讀寫訪問才須要同步化,若是不是共享資源,就不須要同步
鎖重入:synchronized具備鎖重入的功能,當一個線程獲取對象鎖以後,再次請求此對象鎖時是能夠再次獲得該對象的鎖的。存在子父類繼承關係時,子類徹底能夠經過可重入鎖調用父類的同步方法。
當一個線程執行的代碼出現異常時,其所持有的鎖會自動釋放。
同步不具體繼承性,子類方法沒法繼承父類的synchronized關鍵字
2.2. synchronized同步語句塊
synchronized申明方法是有弊端的,只有當方法執行完畢以後纔會釋放對象鎖。使用同步塊的話能夠這樣的問題。
當兩個併發線程訪問同一個對象的synchronized(this) 同步代碼塊時,一段時間內只有一個線程被執行,另外一個線程必須等待當前線程執行完這個代碼執行完之後才能執行該該代碼。
當一個線程訪問objec的synchronized(this)同步代碼塊時,其餘線程對同一個Object中其餘全部的同步代碼塊的訪問都會被阻塞。
synchronized(非this對象)代碼塊的內容和同步方法是異步的。
synchronized關鍵字加到static 靜態方法上時,是給Class加鎖,加在非靜態方法時時給對象加鎖
死鎖:互相等待對方釋放鎖
若是同時持有相同的鎖,那麼這些線程就是同步的,若是分別獲取對象鎖,這些線程就是異步的。
2.3. volatile 關鍵字
主要做用是使變量在多個線程中可見。強制從公共堆棧中取得變量的值,而不是從私有數據棧中取得變得值。
volatile最致命的缺點就是不支持原子性。
原子操做是不能分割的總體,沒有其餘線程可以中斷或者檢查原子操做中的變量。使用AtomicInteger實現原子性
3. 線程間的通訊。
3.1. 等待/通知 機制
經過while循環偵測某一個條件,缺點是浪費CPU資源
經過wait,notify 等待、通知機制實現線程的通訊,wait的做用是使當前執行的線程進行等待。
在調用wait以前,線程必須得到該對象的對象級別鎖,即只能在同步方法或同步快中調用wait方法,在執行wait方法以後,當前線程釋放鎖。
notify也要在同步方法或者同步方法塊中調用,在調用以前,線程也必須得到該對象的對象級別鎖。
執行notify方法以後,當前線程也不會立刻釋放該鎖,呈wait狀態的線程也不會立刻得到該對象鎖,須要等到notify線程將程序執行完畢。
notify能夠隨機喚醒等待隊列中等待同一共享資源的一個線程,並使該線程進入等待狀態。notifyAll方法可使全部的正在等待隊列中等待統一共享資源的的所有線程從等待狀態退出。
方法wait執行完以後,鎖會被自動釋放,可是notify方法被執行以後,鎖不會被釋放,而須要等待線程被執行完。wait(long)方法會在等待必定時間後自動喚醒,固然也能夠notify喚醒
生產者消費者問題建議看書。
3.2.方法join的使用
等待線程對象銷燬執行。方法join具備使線程排隊運行的做用,有點相似同步的效果。
join(long)設置等待的時間。join(long)內部使用wait實現,因此會釋放鎖,sleep(long)卻不釋放鎖。
3.3 類ThreadLocal的使用
爲每一個線程綁定本身的值。數據具備隔離性。
4. lock 的使用
ReenTrantLock類的使用,在須要同步的代碼前使用lock.lock(),結束處使用lock.unlock()釋放鎖
關於這塊,建議看書。主要講等待/通知的另一種實現方式。
能夠參考書籍和慕課網
主要講解
schedule(TimerTask task, Date time)
schedule(TimerTask task, Date firstTime, long period)
schedule(TimerTask task, long delay)
schedule(TimerTask task, long delay, long period)
scheduleAtFixedRate(TimerTask task, long delay, long period)
6. 單例模式和多線程
主要講解 單例模式在多線程中的安全性
7. 拾遺增補
7.1. 線程的狀態
NEW 至今還沒有啓動的
RUNNABLE 正在java虛擬機中執行的線程
BLOCKED 受阻塞並等待某個監控器鎖的線程
WAITTING 無限期的等待另外一個線程來執行某一個特定操做的線程
TIME_WAITING 等待一個線程執行取決於指定等待時間
TERMINATED 已退出的線程
7.2 線程租
能夠將線程歸屬到某一個線程組中,線程組中能夠擁有線程對象,也能夠擁有線程組,組中還能夠有線程。
線程組能夠批量管理線程或者線程組對象,有效的對線程或線程組對象進行組織。