LockSupport(park/unpark)源碼分析

轉(zhuǎn)載:http://www.cnblogs.com/zhizhizhiyuan/p/4966827.html

concurrent包是基于AQS (AbstractQueuedSynchronizer)框架的,AQS框架借助于兩個類:

  • Unsafe(提供CAS操作)
  • LockSupport(提供park/unpark操作)

因此,LockSupport非常重要。

兩個重點

(1)操作對象

歸根結(jié)底,LockSupport.park()和LockSupport.unpark(Thread thread)調(diào)用的是Unsafe中的native代碼:

//LockSupport中
public static void park() {
        UNSAFE.park(false, 0L);
    }
//LockSupport中
public static void unpark(Thread thread) {
        if (thread != null)
            UNSAFE.unpark(thread);
    }

Unsafe類中的對應(yīng)方法:

    //park
    public native void park(boolean isAbsolute, long time);
    
    //unpack
    public native void unpark(Object var1);

park函數(shù)是將當(dāng)前調(diào)用Thread阻塞,而unpark函數(shù)則是將指定線程Thread喚醒。

與Object類的wait/notify機制相比,park/unpark有兩個優(yōu)點:

  • 以thread為操作對象更符合阻塞線程的直觀定義
  • 操作更精準(zhǔn),可以準(zhǔn)確地喚醒某一個線程(notify隨機喚醒一個線程,notifyAll喚醒所有等待的線程),增加了靈活性。

(2)關(guān)于“許可”

在上面的文字中,我使用了阻塞和喚醒,是為了和wait/notify做對比。

  • 其實park/unpark的設(shè)計原理核心是“許可”:park是等待一個許可,unpark是為某線程提供一個許可。
    如果某線程A調(diào)用park,那么除非另外一個線程調(diào)用unpark(A)給A一個許可,否則線程A將阻塞在park操作上。

  • 有一點比較難理解的,是unpark操作可以再park操作之前。
    也就是說,先提供許可。當(dāng)某線程調(diào)用park時,已經(jīng)有許可了,它就消費這個許可,然后可以繼續(xù)運行。這其實是必須的。考慮最簡單的生產(chǎn)者(Producer)消費者(Consumer)模型:Consumer需要消費一個資源,于是調(diào)用park操作等待;Producer則生產(chǎn)資源,然后調(diào)用unpark給予Consumer使用的許可。非常有可能的一種情況是,Producer先生產(chǎn),這時候Consumer可能還沒有構(gòu)造好(比如線程還沒啟動,或者還沒切換到該線程)。那么等Consumer準(zhǔn)備好要消費時,顯然這時候資源已經(jīng)生產(chǎn)好了,可以直接用,那么park操作當(dāng)然可以直接運行下去。如果沒有這個語義,那將非常難以操作。

  • 但是這個“許可”是不能疊加的,“許可”是一次性的。
    比如線程B連續(xù)調(diào)用了三次unpark函數(shù),當(dāng)線程A調(diào)用park函數(shù)就使用掉這個“許可”,如果線程A再次調(diào)用park,則進(jìn)入等待狀態(tài)。

Unsafe.park和Unsafe.unpark的底層實現(xiàn)原理

在Linux系統(tǒng)下,是用的Posix線程庫pthread中的mutex(互斥量),condition(條件變量)來實現(xiàn)的。
mutex和condition保護(hù)了一個_counter的變量,當(dāng)park時,這個變量被設(shè)置為0,當(dāng)unpark時,這個變量被設(shè)置為1。

源碼:
每個Java線程都有一個Parker實例,Parker類是這樣定義的:

class Parker : public os::PlatformParker {  
private:  
  volatile int _counter ;  
  ...  
public:  
  void park(bool isAbsolute, jlong time);  
  void unpark();  
  ...  
}  
class PlatformParker : public CHeapObj<mtInternal> {  
  protected:  
    pthread_mutex_t _mutex [1] ;  
    pthread_cond_t  _cond  [1] ;  
    ...  
}  

可以看到Parker類實際上用Posix的mutex,condition來實現(xiàn)的。
在Parker類里的_counter字段,就是用來記錄“許可”的。

  • park 過程

當(dāng)調(diào)用park時,先嘗試能否直接拿到“許可”,即_counter>0時,如果成功,則把_counter設(shè)置為0,并返回:

void Parker::park(bool isAbsolute, jlong time) {  
  
  // Ideally we'd do something useful while spinning, such  
  // as calling unpackTime().  
  
  // Optional fast-path check:  
  // Return immediately if a permit is available.  
  // We depend on Atomic::xchg() having full barrier semantics  
  // since we are doing a lock-free update to _counter.  
  
  if (Atomic::xchg(0, &_counter) > 0) return;  

如果不成功,則構(gòu)造一個ThreadBlockInVM,然后檢查_counter是不是>0,如果是,則把_counter設(shè)置為0,unlock mutex并返回:

ThreadBlockInVM tbivm(jt);  
if (_counter > 0)  { // no wait needed  
  _counter = 0;  
  status = pthread_mutex_unlock(_mutex);  

否則,再判斷等待的時間,然后再調(diào)用pthread_cond_wait函數(shù)等待,如果等待返回,則把_counter設(shè)置為0,unlock mutex并返回:

if (time == 0) {  
  status = pthread_cond_wait (_cond, _mutex) ;  
}  
_counter = 0 ;  
status = pthread_mutex_unlock(_mutex) ;  
assert_status(status == 0, status, "invariant") ;  
OrderAccess::fence();  
  • unpark 過程

當(dāng)unpark時,則簡單多了,直接設(shè)置_counter為1,再unlock mutex返回。如果_counter之前的值是0,則還要調(diào)用pthread_cond_signal喚醒在park中等待的線程:

void Parker::unpark() {  
  int s, status ;  
  status = pthread_mutex_lock(_mutex);  
  assert (status == 0, "invariant") ;  
  s = _counter;  
  _counter = 1;  
  if (s < 1) {  
     if (WorkAroundNPTLTimedWaitHang) {  
        status = pthread_cond_signal (_cond) ;  
        assert (status == 0, "invariant") ;  
        status = pthread_mutex_unlock(_mutex);  
        assert (status == 0, "invariant") ;  
     } else {  
        status = pthread_mutex_unlock(_mutex);  
        assert (status == 0, "invariant") ;  
        status = pthread_cond_signal (_cond) ;  
        assert (status == 0, "invariant") ;  
     }  
  } else {  
    pthread_mutex_unlock(_mutex);  
    assert (status == 0, "invariant") ;  
  }  
}  
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容