java多線程中用到的方法詳細解析

在多線程學習的過程當中涉及的方法和接口特別多,本文就詳細講解下常用方法的做用和使用場景。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調用,因此不須要擔憂線程間的執行的前後順序。

相關文章
相關標籤/搜索