在多線程學習的過程當中涉及的方法和接口特別多,本文就詳細講解下常用方法的做用和使用場景。java
1.sleep()方法。多線程
當線程對象調用sleep(time)方法後,當前線程會等待指定的時間(time),並讓出cpu執行權,可是它的監控狀態依然當前對象的保持者(不會釋放對象鎖),當指定的時間到了又會自動恢復運行狀態。併發
2.wait()和notify()/notifyAll()方法。ide
wait()和notify()、notifyAll()方法的調用都必須在synchronized修飾的方法或者代碼塊中調用,使用過程當中必須得到對象鎖,不然會拋出java.lang.IllegalMonitorStateException的異常。函數
執行wait()方法,會使當前線程的狀態變爲阻塞狀態並交出對象鎖。工具
執行notify()方法,會隨機挑選一個當前對象阻塞隊列中的線程並將狀態變爲就緒狀態。學習
執行notifyAll()方法,會將當前對象阻塞隊列中的全部線程的狀態變爲就緒狀態。測試
wait()和notify()、notifyAll()爲何都是Object類中的方法。由於synchronized中的這把鎖能夠是任意對象,因此任意對象均可以調用wait()和notify();因此wait和notify屬於Object。spa
專業說:由於這些方法在操做同步線程時,都必需要標識它們操做線程的鎖,只有同一個鎖上的被等待線程,能夠被同一個鎖上的notify喚醒,不能夠對不一樣鎖中的線程進行喚醒。線程
也就是說,等待和喚醒必須是同一個鎖。而鎖能夠是任意對象,因此能夠被任意對象調用的方法是定義在object類中。
3.join()方法。
在A線程中調用了B線程的join()方法時,表示只有當B線程執行完畢時,A線程才能繼續執行。而且join()方法是會釋放鎖的,由於底層使用 wait()
方法來實現的。
4.yield()方法。
yield()讓當前正在運行的線程回到可運行狀態,以容許具備相同優先級的其餘線程得到運行的機會。(不會釋放鎖)
所以,使用yield()的目的是讓具備相同優先級的線程之間可以適當的輪換執行。可是,實際中沒法保證yield()達到讓步的目的,由於,讓步的線程可能被線程調度程序再次選中。
4.interrupt()和isInterrupted()/interrupted()方法。
thread.interrupt(),當thread對象變爲中斷狀態,interrupt()並不能中斷在運行中的線程,它只能改變中斷狀態而已。
thread.interrupted(),判斷當前線程對象的狀態是否爲中斷狀態,內部實現是調用的當前線程的isInterrupted(),而且會重置當前線程的中斷狀態。
thread.isInterrupted(),判斷當前線程對象的狀態是否爲中斷狀態,不會重置當前線程的中斷狀態。
5.CountDownLatch類。
CountDownLatch是一個同步工具類,它容許一個或多個線程一直等待,直到其餘線程的操做執行完後再執行。
1 CountDownLatch countDownLatch=new CountDownLatch(5);//CountDownLatch初始化等待5個線程,等待5個線程執行完主線程再執行。 2 3 countDownLatch.await();//主線程阻塞 4 5 countDownLatch.await(1000, TimeUnit.SECONDS);//主線程阻塞,超時後count尚未爲0的話,主線程執行。 6 7 countDownLatch.countDown();//將count值減1,當count值爲0後,主線程執行。
使用場景:
①某一線程在開始運行前等待n個線程執行完畢。將 CountDownLatch 的計數器初始化爲n :new CountDownLatch(n) ,每當一個任務線程執行完畢,就將計數器減1 countdownlatch.countDown(),當計數器的值變爲0時,在CountDownLatch上 await()的線程就會被喚醒。一個典型應用場景就是啓動一個服務時,主線程須要等待多個組件加載完畢,以後再繼續執行。
②實現多個線程開始執行任務的最大並行性。注意是並行性,不是併發,強調的是多個線程在某一時刻同時開始執行。相似於賽跑,將多個線程放到起點,等待發令槍響,而後同時開跑。作法是初始化一個共享的 CountDownLatch 對象,將其計數器初始化爲 1 :new CountDownLatch(1) ,多個線程在開始執行任務前首先 coundownlatch.await(),當主線程調用 countDown() 時,計數器變爲0,多個線程同時被喚醒。
③死鎖檢測:一個很是方便的使用場景是,你可使用n個線程訪問共享資源,在每次測試階段的線程數目是不一樣的,並嘗試產生死鎖。
6.LockSupport類。
LockSupport能夠起到和wait()同樣的做用。在沒有LockSupport以前,線程的掛起和喚醒我們都是經過Object的wait和notify/notifyAll方法實現。
public class TestObjWait { public static void main(String[] args)throws Exception { Thread A = new Thread(new Runnable() { @Override public void run() { int sum = 0; for(int i=0;i<10;i++){ sum+=i; } LockSupport.park();//阻塞線程 System.out.println(sum); } }); A.start(); //睡眠一秒鐘,保證線程A已經計算完成,阻塞在wait方法 Thread.sleep(1000); LockSupport.unpark(A);//喚醒阻塞線程 } }
總結一下,LockSupport比Object的wait/notify有兩大優點:
①LockSupport不須要在同步代碼塊裏 。因此線程間也不須要維護一個共享的同步對象了,實現了線程間的解耦。
②unpark函數能夠先於park調用,因此不須要擔憂線程間的執行的前後順序。