1. 程式人生 > >Java執行緒的阻塞,就緒和執行

Java執行緒的阻塞,就緒和執行

Java編碼中,經常遇到因為業務邏輯實現的需要會對執行緒進行人為的狀態轉換操作,最常用的就是阻塞,之前雖然用過阻塞,但是一直沒有進行系統的總結。

總的來說,對執行緒狀態的轉換操作分成兩大類:

Thread類的sleep,yield,join 方法

Object類的wait,notify,notifyall 方法

之前不熟悉時總是把sleep和wait混淆起來,其實兩者的使用場景和機制差別很大。下面分別一一說明.

Sleep(longtime)

Sleep是帶時間引數的,代表休眠多久,一般用法為Thread.sleep(3000),或者Thread.currentThread().sleep(1000);使用的是Thread的靜態成員方法sleep,基本上上面兩種用法可以使用在程式碼的任何地方,包括main方法和其他成員方法中,這是為什麼呢,我網上查了很久沒有找到底層的實現邏輯,只能簡單認為在java中,任何程式碼都是線上程中執行的,包括main方法是在主執行緒中執行的,所以在程式碼的任何地方都能通過執行緒類Thread的靜態方法進行阻塞操作。

Yield()

Yield方法沒有引數,它的作用就是把現在正在執行的執行緒的狀態由執行轉成就緒狀態,等待資源重新執行。除非cpu資源很緊張,否則一般執行緒呼叫Yield()後回立刻獲得資源執行,所以基本上看不出有什麼效果,而這個方法也比較少用。這裡提下執行緒的三種狀態的區別:阻塞,就緒,執行,執行很好理解,就是使用cpu執行程式碼指令,阻塞和就緒區別就比較明顯,阻塞是被外界事件所阻塞的,比如我們在程式碼中呼叫了執行緒阻塞的方法Sleep或者wait,或者是被物件鎖阻塞,簡而言之就是不是因為cpu資源不足而不能執行,而是被其他事件所阻塞,等待事件完成或者通知後,轉成就緒狀態,獲取到cpu資源就轉成執行狀態。

Join()

Join方法也是編碼中常用的阻塞方法,主要是在主執行緒中等待其他子執行緒執行完run方法後再繼續執行主執行緒的後續程式碼,當然也可以用到其他執行緒中。

         上面所述都是Tread本身的阻塞機制,而下面所述的是關於和物件鎖相關的,或者說和同步鎖synchronized相關的

Wait()

Wait方法可以帶時間引數也可以不帶引數,一般用法是在synchronized語句塊中,例如

synchronized(obj){

         obj.wait(1000);

}

帶引數時是阻塞1秒後無人喚醒則自動恢復就緒狀態,無引數是指一直阻塞該執行緒知道有人喚醒。

Notify()

用法如下

synchronized(obj){

         obj.Notify();

}

隨機喚醒一個因為呼叫obj.wait()而阻塞等待的執行緒。

Notifyall()

用法如下

synchronized(obj){

         obj.Notifyall();

}

喚醒所有因為呼叫obj.wait()而阻塞等待的執行緒。

Sleep和wait的區別:

Sleep阻塞當前執行緒,但是並不會釋放cpu資源,並且一定休眠時間結束後才能執行後續語句,並不能被中途喚醒。而且不釋放物件鎖資源,正常,因為它跟物件鎖毫無關係。

Wait只有在獲取物件鎖的情況下才能使用,說白了就是必須在synchronized(obj)語句塊裡執行,阻塞當前執行緒,並釋放cpu資源,中途可以被其他執行緒呼叫Notify喚醒。並在阻塞的時間內,釋放obj的物件鎖,這樣其他執行緒可以在此期間內獲取物件鎖從而執行synchronized裡面的語句塊,跟物件鎖關係密切。

一般使用場景,如果是單線執行緒,比較簡單地阻塞當前執行緒,效率要求不高的話,使用sleep即可,如果併發很大,效率要求比較高的話,使用obj.wait()來提高cpu的使用效率。