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


在多線程學習的過程中涉及的方法和接口特別多,本文就詳細講解下經常使用方法的作用和使用場景。

1.sleep()方法。

     當線程對象調用sleep(time)方法后,當前線程會等待指定的時間(time),並讓出cpu執行權,但是它的監控狀態依然當前對象的保持者(不會釋放對象鎖),當指定的時間到了又會自動恢復運行狀態。

2.wait()和notify()/notifyAll()方法。

    wait()和notify()、notifyAll()方法的調用都必須在synchronized修飾的方法或者代碼塊中調用,使用過程中必須獲得對象鎖,否則會拋出java.lang.IllegalMonitorStateException的異常。

    執行wait()方法,會使當前線程的狀態變為阻塞狀態並交出對象鎖。

    執行notify()方法,會隨機挑選一個當前對象阻塞隊列中的線程並將狀態變為就緒狀態。

    執行notifyAll()方法,會將當前對象阻塞隊列中的所有線程的狀態變為就緒狀態。

    wait()和notify()、notifyAll()為什么都是Object類中的方法。因為synchronized中的這把鎖可以是任意對象,所以任意對象都可以調用wait()和notify();所以wait和notify屬於Object。

   專業說:因為這些方法在操作同步線程時,都必須要標識它們操作線程的鎖,只有同一個鎖上的被等待線程,可以被同一個鎖上的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調用,所以不需要擔心線程間的執行的先后順序。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM