精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

面試多線程同步,你必須要思考的問題

新聞
ReentrantLock的實(shí)現(xiàn)網(wǎng)上有很多文章了,本篇文章會簡單介紹下其java層實(shí)現(xiàn),重點(diǎn)放在分析競爭鎖失敗后如何阻塞線程。

 ReentrantLock的實(shí)現(xiàn)網(wǎng)上有很多文章了,本篇文章會簡單介紹下其java層實(shí)現(xiàn),重點(diǎn)放在分析競爭鎖失敗后如何阻塞線程。 因篇幅有限,synchronized的內(nèi)容將會放到下篇文章。

[[272324]]

Java Lock的實(shí)現(xiàn)

ReentrantLock是jdk中常用的鎖實(shí)現(xiàn),其實(shí)現(xiàn)邏輯主語基于AQS(juc包中的大多數(shù)同步類實(shí)現(xiàn)都是基于AQS);接下來會簡單介紹AQS的大致原理,關(guān)于其實(shí)現(xiàn)細(xì)節(jié)以及各種應(yīng)用,之后會寫一篇文章具體分析。

AQS

AQS是類AbstractQueuedSynchronizer.java的簡稱,JUC包下的ReentrantLock、CyclicBarrier、CountdownLatch都使用到了AQS。

其大致原理如下:

  1. AQS維護(hù)一個叫做state的int型變量和一個雙向鏈表,state用來表示同步狀態(tài),雙向鏈表存儲的是等待鎖的線程
  2. 加鎖時首先調(diào)用tryAcquire嘗試獲得鎖,如果獲得鎖失敗,則將線程插入到雙向鏈表中,并調(diào)用LockSupport.park()方法阻塞當(dāng)前線程。
  3. 釋放鎖時調(diào)用LockSupport.unpark()喚起鏈表中的第一個節(jié)點(diǎn)的線程。被喚起的線程會重新走一遍競爭鎖的流程。

其中tryAcquire方法是抽象方法,具體實(shí)現(xiàn)取決于實(shí)現(xiàn)類,我們常說的公平鎖和非公平鎖的區(qū)別就在于該方法的實(shí)現(xiàn)。

ReentrantLock

ReentrantLock分為公平鎖和非公平鎖,我們只看公平鎖。 ReentrantLock.lock會調(diào)用到ReentrantLock#FairSync.lock中:

FairSync.java

  1. static final class FairSync extends Sync { 
  2.  final void lock() { 
  3.  acquire(1); 
  4.  } 
  5.  /** 
  6.  * Fair version of tryAcquire. Don't grant access unless 
  7.  * recursive call or no waiters or is first
  8.  */ 
  9.  protected final boolean tryAcquire(int acquires) { 
  10.  final Thread current = Thread.currentThread(); 
  11.  int c = getState(); 
  12.  if (c == 0) { 
  13.  if (!hasQueuedPredecessors() && 
  14.  compareAndSetState(0, acquires)) { 
  15.  setExclusiveOwnerThread(current); 
  16.  return true
  17.  } 
  18.  } 
  19.  else if (current == getExclusiveOwnerThread()) { 
  20.  int nextc = c + acquires; 
  21.  if (nextc < 0) 
  22.  throw new Error("Maximum lock count exceeded"); 
  23.  setState(nextc); 
  24.  return true
  25.  } 
  26.  return false
  27.  } 
  28.  } 

AbstractQueuedSynchronizer.java

  1. public final void acquire(int arg) { 
  2.  if (!tryAcquire(arg) && 
  3.  acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) 
  4.  selfInterrupt(); 
  5.  } 

可以看到FairSync.lock調(diào)用了AQS的acquire方法,而在acquire中首先調(diào)用tryAcquire嘗試獲得鎖,以下兩種情況返回true:

  1. state==0(代表沒有線程持有鎖),且等待隊(duì)列為空(公平的實(shí)現(xiàn)),且cas修改state成功。
  2. 當(dāng)前線程已經(jīng)獲得了鎖,這次調(diào)用是重入

如果tryAcquire失敗則調(diào)用acquireQueued阻塞當(dāng)前線程。acquireQueued最終會調(diào)用到LockSupport.park()阻塞線程。

LockSupport.park

個人認(rèn)為,要深入理解鎖機(jī)制,一個很重要的點(diǎn)是理解系統(tǒng)是如何阻塞線程的。

LockSupport.java

  1. public static void park(Object blocker) { 
  2.  Thread t = Thread.currentThread(); 
  3.  setBlocker(t, blocker); 
  4.  UNSAFE.park(false, 0L); 
  5.  setBlocker(t, null); 

park方法的參數(shù)blocker是用于負(fù)責(zé)這次阻塞的同步對象,在AQS的調(diào)用中,這個對象就是AQS本身。我們知道synchronized關(guān)鍵字是需要指定一個對象的(如果作用于方法上則是當(dāng)前對象或當(dāng)前類),與之類似blocker就是LockSupport指定的對象。

park方法調(diào)用了native方法UNSAFE.park,第一個參數(shù)代表第二個參數(shù)是否是絕對時間,第二個參數(shù)代表最長阻塞時間。

其實(shí)現(xiàn)如下,只保留核心代碼,完整代碼看查看unsafe.cpp

  1. Unsafe_Park(JNIEnv *env, jobject unsafe, jboolean isAbsolute, jlong time){ 
  2. ... 
  3. thread->parker()->park(isAbsolute != 0, time); 
  4. ... 

park方法在os_linux.cpp中(其他操作系統(tǒng)的實(shí)現(xiàn)在os_xxx中)

  1. void Parker::park(bool isAbsolute, jlong time) { 
  2.  ... 
  3.  //獲得當(dāng)前線程 
  4.  Thread* thread = Thread::current(); 
  5.  assert(thread->is_Java_thread(), "Must be JavaThread"); 
  6.  JavaThread *jt = (JavaThread *)thread; 
  7.  //如果當(dāng)前線程被設(shè)置了interrupted標(biāo)記,則直接返回 
  8.  if (Thread::is_interrupted(thread, false)) { 
  9.  return
  10.  } 
  11.  if (time > 0) { 
  12.  //unpacktime中根據(jù)isAbsolute的值來填充absTime結(jié)構(gòu)體,isAbsolute為true時,time代表絕對時間且單位是毫秒,否則time是相對時間且單位是納秒 
  13.  //absTime.tvsec代表了對于時間的秒 
  14.  //absTime.tv_nsec代表對應(yīng)時間的納秒 
  15.  unpackTime(&absTime, isAbsolute, time); 
  16.  } 
  17.  //調(diào)用mutex trylock方法 
  18.  if (Thread::is_interrupted(thread, false) || pthread_mutex_trylock(_mutex) != 0) { 
  19.  return
  20.  } 
  21.  //_counter是一個許可的數(shù)量,跟ReentrantLock里定義的許可變量基本都是一個原理。 unpack方法調(diào)用時會將_counter賦值為1。 
  22.  //_counter>0代表已經(jīng)有人調(diào)用了unpark,所以不用阻塞 
  23.  int status ; 
  24.  if (_counter > 0) { // no wait needed 
  25.  _counter = 0; 
  26.  //釋放mutex鎖 
  27.  status = pthread_mutex_unlock(_mutex); 
  28.  return
  29.  } 
  30. //設(shè)置線程狀態(tài)為CONDVAR_WAIT 
  31.  OSThreadWaitState osts(thread->osthread(), false /* not Object.wait() */); 
  32.  ... 
  33.  //等待 
  34.  _cur_index = isAbsolute ? ABS_INDEX : REL_INDEX; 
  35.  pthread_cond_timedwait(&_cond[_cur_index], _mutex, &absTime); 
  36.  ... 
  37.  //釋放mutex鎖 
  38.  status = pthread_mutex_unlock(_mutex) ; 

park方法用POSIX的pthread_cond_timedwait方法阻塞線程,調(diào)用pthread_cond_timedwait前需要先獲得鎖,因此park主要流程為:

  1. 調(diào)用pthread_mutex_trylock嘗試獲得鎖,如果獲取鎖失敗則直接返回
  2. 調(diào)用pthread_cond_timedwait進(jìn)行等待
  3. 調(diào)用pthread_mutex_unlock釋放鎖

另外,在阻塞當(dāng)前線程前,會調(diào)用OSThreadWaitState的構(gòu)造方法將線程狀態(tài)設(shè)置為CONDVAR_WAIT,在Jvm中Thread狀態(tài)枚舉如下

  1.  enum ThreadState { 
  2.  ALLOCATED, // Memory has been allocated but not initialized 
  3.  INITIALIZED, // The thread has been initialized but yet started 
  4.  RUNNABLE, // Has been started and is runnable, but not necessarily running 
  5.  MONITOR_WAIT, // Waiting on a contended monitor lock 
  6.  CONDVAR_WAIT, // Waiting on a condition variable 
  7.  OBJECT_WAIT, // Waiting on an Object.wait() call 
  8.  BREAKPOINTED, // Suspended at breakpoint 
  9.  SLEEPING, // Thread.sleep() 
  10.  ZOMBIE // All done, but not reclaimed yet 
  11. }; 

Linux的timedwait

由上文我們可以知道LockSupport.park方法最終是由POSIX的 pthread_cond_timedwait的方法實(shí)現(xiàn)的。 我們現(xiàn)在就進(jìn)一步看看pthread_mutex_trylock,pthread_cond_timedwait,pthread_mutex_unlock這幾個方法是如何實(shí)現(xiàn)的。

Linux系統(tǒng)中相關(guān)代碼在glibc庫中。

pthread_mutex_trylock

先看trylock的實(shí)現(xiàn), 代碼在glibc的pthread_mutex_trylock.c文件中,該方法代碼很多,我們只看主要代碼

  1. //pthread_mutex_t是posix中的互斥鎖結(jié)構(gòu)體 
  2. int 
  3. __pthread_mutex_trylock (mutex) 
  4.  pthread_mutex_t *mutex; 
  5.  int oldval; 
  6.  pid_t id = THREAD_GETMEM (THREAD_SELF, tid); 
  7. switch (__builtin_expect (PTHREAD_MUTEX_TYPE (mutex), 
  8.  PTHREAD_MUTEX_TIMED_NP)) 
  9.  { 
  10.  case PTHREAD_MUTEX_ERRORCHECK_NP: 
  11.  case PTHREAD_MUTEX_TIMED_NP: 
  12.  case PTHREAD_MUTEX_ADAPTIVE_NP: 
  13.  /* Normal mutex. */ 
  14.  if (lll_trylock (mutex->__data.__lock) != 0) 
  15.  break; 
  16.  /* Record the ownership. */ 
  17.  mutex->__data.__owner = id; 
  18.  ++mutex->__data.__nusers; 
  19.  return 0; 
  20.  } 
  21. }  
  22.  //以下代碼在lowlevellock.h中  
  23.  #define __lll_trylock(futex) \ 
  24.  (atomic_compare_and_exchange_val_acq (futex, 1, 0) != 0) 
  25.  #define lll_trylock(futex) __lll_trylock (&(futex)) 

mutex默認(rèn)用的是PTHREAD_MUTEX_NORMAL類型(與PTHREAD_MUTEX_TIMED_NP相同); 因此會先調(diào)用lll_trylock方法,lll_trylock實(shí)際上是一個cas操作,如果mutex->data.lock==0則將其修改為1并返回0,否則返回1。

如果成功,則更改mutex中的owner為當(dāng)前線程。

pthread_mutex_unlock

pthread_mutex_unlock.c

  1. int 
  2. internal_function attribute_hidden 
  3. __pthread_mutex_unlock_usercnt (mutex, decr) 
  4.  pthread_mutex_t *mutex; 
  5.  int decr; 
  6.  if (__builtin_expect (type, PTHREAD_MUTEX_TIMED_NP) 
  7.  == PTHREAD_MUTEX_TIMED_NP) 
  8.  { 
  9.  /* Always reset the owner field. */ 
  10.  normal: 
  11.  mutex->__data.__owner = 0; 
  12.  if (decr) 
  13.  /* One less user. */ 
  14.  --mutex->__data.__nusers; 
  15.  /* Unlock. */ 
  16.  lll_unlock (mutex->__data.__lock, PTHREAD_MUTEX_PSHARED (mutex)); 
  17.  return 0; 
  18.  } 
  19.  } 

pthread_mutex_unlock將mutex中的owner清空,并調(diào)用了lll_unlock方法

lowlevellock.h

  1.  #define __lll_unlock(futex, private) \ 
  2.  ((void) ({ \ 
  3.  int *__futex = (futex); \ 
  4.  int __val = atomic_exchange_rel (__futex, 0); \ 
  5.  \ 
  6.  if (__builtin_expect (__val > 1, 0)) \ 
  7.  lll_futex_wake (__futex, 1, private); \ 
  8.  })) 
  9. #define lll_unlock(futex, private) __lll_unlock(&(futex), private) 
  10. #define lll_futex_wake(ftx, nr, private) \ 
  11. ({ \ 
  12.  DO_INLINE_SYSCALL(futex, 3, (long) (ftx), \ 
  13.  __lll_private_flag (FUTEX_WAKE, private), \ 
  14.  (int) (nr)); \ 
  15.  _r10 == -1 ? -_retval : _retval; \ 
  16. }) 

lll_unlock分為兩個步驟:

  1. 將futex設(shè)置為0并拿到設(shè)置之前的值(用戶態(tài)操作)
  2. 如果futex之前的值>1,代表存在鎖沖突,也就是說有線程調(diào)用了FUTEX_WAIT在休眠,所以通過調(diào)用系統(tǒng)函數(shù)FUTEX_WAKE喚醒休眠線程

FUTEX_WAKE在上一篇文章有分析,futex機(jī)制的核心是當(dāng)獲得鎖時,嘗試cas更改一個int型變量(用戶態(tài)操作),如果integer原始值是0,則修改成功,該線程獲得鎖,否則就將當(dāng)期線程放入到 wait queue中,wait queue中的線程不會被系統(tǒng)調(diào)度(內(nèi)核態(tài)操作)。

futex變量的值有3種:0代表當(dāng)前鎖空閑,1代表有線程持有當(dāng)前鎖,2代表存在鎖沖突。futex的值初始化時是0;當(dāng)調(diào)用try_lock的時候會利用cas操作改為1(見上面的trylock函數(shù));當(dāng)調(diào)用lll_lock時,如果不存在鎖沖突,則將其改為1,否則改為2。

  1. #define __lll_lock(futex, private) \ 
  2.  ((void) ({ \ 
  3.  int *__futex = (futex); \ 
  4.  if (__builtin_expect (atomic_compare_and_exchange_bool_acq (__futex, \ 
  5.  1, 0), 0)) \ 
  6.  { \ 
  7.  if (__builtin_constant_p (private) && (private) == LLL_PRIVATE) \ 
  8.  __lll_lock_wait_private (__futex); \ 
  9.  else \ 
  10.  __lll_lock_wait (__futex, private); \ 
  11.  } \ 
  12.  })) 
  13. #define lll_lock(futex, private) __lll_lock (&(futex), private) 
  14. void 
  15. __lll_lock_wait_private (int *futex) 
  16. //第一次進(jìn)來的時候futex==1,所以不會走這個if 
  17.  if (*futex == 2) 
  18.  lll_futex_wait (futex, 2, LLL_PRIVATE); 
  19. //在這里會把futex設(shè)置成2,并調(diào)用futex_wait讓當(dāng)前線程等待 
  20.  while (atomic_exchange_acq (futex, 2) != 0) 
  21.  lll_futex_wait (futex, 2, LLL_PRIVATE); 

pthread_cond_timedwait

pthread_cond_timedwait用于阻塞線程,實(shí)現(xiàn)線程等待, 代碼在glibc的pthread_cond_timedwait.c文件中,代碼較長,你可以先簡單過一遍,看完下面的分析再重新讀一遍代碼

  1. int 
  2. int 
  3. __pthread_cond_timedwait (cond, mutex, abstime) 
  4.  pthread_cond_t *cond; 
  5.  pthread_mutex_t *mutex; 
  6.  const struct timespec *abstime; 
  7.  struct _pthread_cleanup_buffer buffer; 
  8.  struct _condvar_cleanup_buffer cbuffer; 
  9.  int result = 0; 
  10.  /* Catch invalid parameters. */ 
  11.  if (abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000) 
  12.  return EINVAL; 
  13.  int pshared = (cond->__data.__mutex == (void *) ~0l) 
  14.  ? LLL_SHARED : LLL_PRIVATE; 
  15.  //1.獲得cond鎖 
  16.  lll_lock (cond->__data.__lock, pshared); 
  17.  //2.釋放mutex鎖 
  18.  int err = __pthread_mutex_unlock_usercnt (mutex, 0); 
  19.  if (err) 
  20.  { 
  21.  lll_unlock (cond->__data.__lock, pshared); 
  22.  return err; 
  23.  } 
  24.  /* We have one new user of the condvar. */ 
  25.  //每執(zhí)行一次wait(pthread_cond_timedwait/pthread_cond_wait),__total_seq就會+1 
  26.  ++cond->__data.__total_seq; 
  27.  //用來執(zhí)行futex_wait的變量 
  28.  ++cond->__data.__futex; 
  29.  //標(biāo)識該cond還有多少線程在使用,pthread_cond_destroy需要等待所有的操作完成 
  30.  cond->__data.__nwaiters += 1 << COND_NWAITERS_SHIFT; 
  31.  /* Remember the mutex we are using here. If there is already a 
  32.  different address store this is a bad user bug. Do not store 
  33.  anything for pshared condvars. */ 
  34.  //保存mutex鎖 
  35.  if (cond->__data.__mutex != (void *) ~0l) 
  36.  cond->__data.__mutex = mutex; 
  37.  /* Prepare structure passed to cancellation handler. */ 
  38.  cbuffer.cond = cond; 
  39.  cbuffer.mutex = mutex; 
  40.  /* Before we block we enable cancellation. Therefore we have to 
  41.  install a cancellation handler. */ 
  42.  __pthread_cleanup_push (&buffer, __condvar_cleanup, &cbuffer); 
  43.  /* The current values of the wakeup counter. The "woken" counter 
  44.  must exceed this value. */ 
  45.  //記錄futex_wait前的__wakeup_seq(為該cond上執(zhí)行了多少次sign操作+timeout次數(shù))和__broadcast_seq(代表在該cond上執(zhí)行了多少次broadcast) 
  46.  unsigned long long int val; 
  47.  unsigned long long int seq; 
  48.  val = seq = cond->__data.__wakeup_seq; 
  49.  /* Remember the broadcast counter. */ 
  50.  cbuffer.bc_seq = cond->__data.__broadcast_seq; 
  51.  while (1) 
  52.  { 
  53.  //3.計(jì)算要wait的相對時間 
  54.  struct timespec rt; 
  55.  { 
  56. #ifdef __NR_clock_gettime 
  57.  INTERNAL_SYSCALL_DECL (err); 
  58.  int ret; 
  59.  ret = INTERNAL_VSYSCALL (clock_gettime, err, 2, 
  60.  (cond->__data.__nwaiters 
  61.  & ((1 << COND_NWAITERS_SHIFT) - 1)), 
  62.  &rt); 
  63. # ifndef __ASSUME_POSIX_TIMERS 
  64.  if (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (ret, err), 0)) 
  65.  { 
  66.  struct timeval tv; 
  67.  (void) gettimeofday (&tv, NULL); 
  68.  /* Convert the absolute timeout value to a relative timeout. */ 
  69.  rt.tv_sec = abstime->tv_sec - tv.tv_sec; 
  70.  rt.tv_nsec = abstime->tv_nsec - tv.tv_usec * 1000; 
  71.  } 
  72.  else 
  73. # endif 
  74.  { 
  75.  /* Convert the absolute timeout value to a relative timeout. */ 
  76.  rt.tv_sec = abstime->tv_sec - rt.tv_sec; 
  77.  rt.tv_nsec = abstime->tv_nsec - rt.tv_nsec; 
  78.  } 
  79. #else 
  80.  /* Get the current time. So far we support only one clock. */ 
  81.  struct timeval tv; 
  82.  (void) gettimeofday (&tv, NULL); 
  83.  /* Convert the absolute timeout value to a relative timeout. */ 
  84.  rt.tv_sec = abstime->tv_sec - tv.tv_sec; 
  85.  rt.tv_nsec = abstime->tv_nsec - tv.tv_usec * 1000; 
  86. #endif 
  87.  } 
  88.  if (rt.tv_nsec < 0) 
  89.  { 
  90.  rt.tv_nsec += 1000000000; 
  91.  --rt.tv_sec; 
  92.  } 
  93.  /*---計(jì)算要wait的相對時間 end---- */ 
  94.  //是否超時 
  95.  /* Did we already time out? */ 
  96.  if (__builtin_expect (rt.tv_sec < 0, 0)) 
  97.  { 
  98.  //被broadcast喚醒,這里疑問的是,為什么不需要判斷__wakeup_seq? 
  99.  if (cbuffer.bc_seq != cond->__data.__broadcast_seq) 
  100.  goto bc_out; 
  101.  goto timeout; 
  102.  } 
  103.  unsigned int futex_val = cond->__data.__futex; 
  104.  //4.釋放cond鎖,準(zhǔn)備wait 
  105.  lll_unlock (cond->__data.__lock, pshared); 
  106.  /* Enable asynchronous cancellation. Required by the standard. */ 
  107.  cbuffer.oldtype = __pthread_enable_asynccancel (); 
  108.  //5.調(diào)用futex_wait 
  109.  /* Wait until woken by signal or broadcast. */ 
  110.  err = lll_futex_timed_wait (&cond->__data.__futex, 
  111.  futex_val, &rt, pshared); 
  112.  /* Disable asynchronous cancellation. */ 
  113.  __pthread_disable_asynccancel (cbuffer.oldtype); 
  114.  //6.重新獲得cond鎖,因?yàn)橛忠L問&修改cond的數(shù)據(jù)了 
  115.  lll_lock (cond->__data.__lock, pshared); 
  116.  //__broadcast_seq值發(fā)生改變,代表發(fā)生了有線程調(diào)用了廣播 
  117.  if (cbuffer.bc_seq != cond->__data.__broadcast_seq) 
  118.  goto bc_out; 
  119.  //判斷是否是被sign喚醒的,sign會增加__wakeup_seq 
  120.  //第二個條件cond->__data.__woken_seq != val的意義在于 
  121.  //可能兩個線程A、B在wait,一個線程調(diào)用了sign導(dǎo)致A被喚醒,這時B因?yàn)槌瑫r被喚醒 
  122.  //對于B線程來說,執(zhí)行到這里時第一個條件也是滿足的,從而導(dǎo)致上層拿到的result不是超時 
  123.  //所以這里需要判斷下__woken_seq(即該cond已經(jīng)被喚醒的線程數(shù))是否等于__wakeup_seq(sign執(zhí)行次數(shù)+timeout次數(shù)) 
  124.  val = cond->__data.__wakeup_seq; 
  125.  if (val != seq && cond->__data.__woken_seq != val) 
  126.  break; 
  127.  /* Not woken yet. Maybe the time expired? */ 
  128.  if (__builtin_expect (err == -ETIMEDOUT, 0)) 
  129.  { 
  130.  timeout: 
  131.  /* Yep. Adjust the counters. */ 
  132.  ++cond->__data.__wakeup_seq; 
  133.  ++cond->__data.__futex; 
  134.  /* The error value. */ 
  135.  result = ETIMEDOUT; 
  136.  break; 
  137.  } 
  138.  } 
  139.  //一個線程已經(jīng)醒了所以這里__woken_seq +1 
  140.  ++cond->__data.__woken_seq; 
  141.  bc_out: 
  142.  // 
  143.  cond->__data.__nwaiters -= 1 << COND_NWAITERS_SHIFT; 
  144.  /* If pthread_cond_destroy was called on this variable already, 
  145.  notify the pthread_cond_destroy caller all waiters have left 
  146.  and it can be successfully destroyed. */ 
  147.  if (cond->__data.__total_seq == -1ULL 
  148.  && cond->__data.__nwaiters < (1 << COND_NWAITERS_SHIFT)) 
  149.  lll_futex_wake (&cond->__data.__nwaiters, 1, pshared); 
  150.  //9.cond數(shù)據(jù)修改完畢,釋放鎖 
  151.  lll_unlock (cond->__data.__lock, pshared); 
  152.  /* The cancellation handling is back to normal, remove the handler. */ 
  153.  __pthread_cleanup_pop (&buffer, 0); 
  154.  //10.重新獲得mutex鎖 
  155.  err = __pthread_mutex_cond_lock (mutex); 
  156.  return err ?: result; 

上面的代碼雖然加了注釋,但相信大多數(shù)人第一次看都看不懂。 我們來簡單梳理下,上面代碼有兩把鎖,一把是mutex鎖,一把cond鎖。另外,在調(diào)用pthread_cond_timedwait前后必須調(diào)用pthread_mutex_lock(&mutex);和pthread_mutex_unlock(&mutex);加/解mutex鎖。

因此pthread_cond_timedwait的使用大致分為幾個流程:

  1. 加mutex鎖(在pthread_cond_timedwait調(diào)用前)
  2. 加cond鎖
  3. 釋放mutex鎖
  4. 修改cond數(shù)據(jù)
  5. 釋放cond鎖
  6. 執(zhí)行futex_wait
  7. 重新獲得cond鎖
  8. 比較cond的數(shù)據(jù),判斷當(dāng)前線程是被正常喚醒的還是timeout喚醒的,需不需要重新wait
  9. 修改cond數(shù)據(jù)
  10. 是否cond鎖
  11. 重新獲得mutex鎖
  12. 釋放mutex鎖(在pthread_cond_timedwait調(diào)用后)

看到這里,你可能有幾點(diǎn)疑問:為什么需要兩把鎖?mutex鎖和cond鎖的作用是什么?

mutex鎖

說mutex鎖的作用之前,我們回顧一下java的Object.wait的使用。Object.wait必須是在synchronized同步塊中使用。試想下如果不加synchronized也能運(yùn)行Object.wait的話會存在什么問題?

  1. Object condObj=new Object(); 
  2. voilate int flag = 0; 
  3. public void waitTest(){ 
  4.  if(flag == 0){ 
  5.  condObj.wait(); 
  6.  } 
  7. public void notifyTest(){ 
  8.  flag=1; 
  9.  condObj.notify(); 

如上代碼,A線程調(diào)用waitTest,這時flag==0,所以準(zhǔn)備調(diào)用wait方法進(jìn)行休眠,這時B線程開始執(zhí)行,調(diào)用notifyTest將flag置為1,并調(diào)用notify方法,注意:此時A線程還沒調(diào)用wait,所以notfiy沒有喚醒任何線程。然后A線程繼續(xù)執(zhí)行,調(diào)用wait方法進(jìn)行休眠,而之后不會有人來喚醒A線程,A線程將永久wait下去!

  1. Object condObj=new Object(); 
  2. voilate int flag = 0; 
  3. public void waitTest(){ 
  4.  synchronized(condObj){ 
  5.  if(flag == 0){ 
  6.  condObj.wait(); 
  7.  } 
  8.  } 
  9. public void notifyTest(){ 
  10.  synchronized(condObj){ 
  11.  flag=1; 
  12.  condObj.notify(); 
  13.  } 

在有鎖保護(hù)下的情況下, 當(dāng)調(diào)用condObj.wait時,flag一定是等于0的,不會存在一直wait的問題。

回到pthread_cond_timedwait,其需要加mutex鎖的原因就呼之欲出了: 保證wait和其wait條件的原子性

不管是glibc的pthread_cond_timedwait/pthread_cond_signal還是java層的Object.wait/Object.notify,Jdk AQS的Condition.await/Condition.signal,所有的Condition機(jī)制都需要在加鎖環(huán)境下才能使用,其根本原因就是要保證進(jìn)行線程休眠時,條件變量是沒有被篡改的。

注意下mutex鎖釋放的時機(jī),回顧上文中pthread_cond_timedwait的流程,在第2步時就釋放了mutex鎖,之后調(diào)用futex_wait進(jìn)行休眠,為什么要在休眠前就釋放mutex鎖呢?原因也很簡單:如果不釋放mutex鎖就開始休眠,那其他線程就永遠(yuǎn)無法調(diào)用signal方法將休眠線程喚醒(因?yàn)檎{(diào)用signal方法前需要獲得mutex鎖)。

在線程被喚醒之后還要在第10步中重新獲得mutex鎖是為了保證鎖的語義(思考下如果不重新獲得mutex鎖會發(fā)生什么)。

cond鎖

cond鎖的作用其實(shí)很簡單: 保證對象cond->data的線程安全。 在pthread_cond_timedwait時需要修改cond->data的數(shù)據(jù),如增加total_seq(在這個cond上一共執(zhí)行過多少次wait)增加nwaiters(現(xiàn)在還有多少個線程在wait這個cond),所有在修改及訪問cond->data時需要加cond鎖。

這里我沒想明白的一點(diǎn)是,用mutex鎖也能保證cond->data修改的線程安全,只要晚一點(diǎn)釋放mutex鎖就行了。為什么要先釋放mutex,重新獲得cond來保證線程安全? 是為了避免mutex鎖住的范圍太大嗎?

該問題的答案可以見評論區(qū)@11800222 的回答:

mutex鎖不能保護(hù)cond->data修改的線程安全,調(diào)用signal的線程沒有用mutex鎖保護(hù)修改cond的那段臨界區(qū)。

pthread_cond_wait/signal這一對本身用cond鎖同步就能睡眠喚醒。 wait的時候需要傳入mutex是因?yàn)樗咔靶枰尫舖utex鎖,但睡眠之前又不能有無鎖的空隙,解決辦法是讓mutex鎖在cond鎖上之后再釋放。 而signal前不需要釋放mutex鎖,在持有mutex的情況下signal,之后再釋放mutex鎖。

如何喚醒休眠線程

喚醒休眠線程的代碼比較簡單,主要就是調(diào)用lll_futex_wake。

  1. int 
  2. __pthread_cond_signal (cond) 
  3.  pthread_cond_t *cond; 
  4.  int pshared = (cond->__data.__mutex == (void *) ~0l) 
  5.  ? LLL_SHARED : LLL_PRIVATE; 
  6.  //因?yàn)橐僮鱟ond的數(shù)據(jù),所以要加鎖 
  7.  lll_lock (cond->__data.__lock, pshared); 
  8.  /* Are there any waiters to be woken? */ 
  9.  if (cond->__data.__total_seq > cond->__data.__wakeup_seq) 
  10.  { 
  11.  //__wakeup_seq為執(zhí)行sign與timeout次數(shù)的和 
  12.  ++cond->__data.__wakeup_seq; 
  13.  ++cond->__data.__futex; 
  14.  ... 
  15.  //喚醒wait的線程 
  16.  lll_futex_wake (&cond->__data.__futex, 1, pshared); 
  17.  } 
  18.  /* We are done. */ 
  19.  lll_unlock (cond->__data.__lock, pshared); 
  20.  return 0; 

End

本文對Java簡單介紹了ReentrantLock實(shí)現(xiàn)原理,對LockSupport.park底層實(shí)現(xiàn)pthread_cond_timedwait機(jī)制做了詳細(xì)分析。

責(zé)任編輯:華軒 來源: java520
相關(guān)推薦

2019-02-18 13:36:03

Redis數(shù)據(jù)庫面試

2011-06-22 13:57:54

Java多線程

2011-06-22 13:47:16

Java多線程

2019-08-06 14:54:22

Hadoop數(shù)據(jù)集海量數(shù)據(jù)

2022-09-05 17:49:53

Java線程池

2009-07-01 17:34:03

Servlet和JSP

2019-03-23 20:00:04

面試react.js前端

2018-05-30 16:55:47

阿里Java多線程

2018-09-21 11:11:34

備份離線自動

2013-05-29 10:47:50

Android開發(fā)Java多線程java面試題

2018-11-08 12:07:38

備份手動磁盤

2018-11-28 10:00:42

React組件前端

2019-06-20 17:39:12

Android啟動優(yōu)化

2019-05-16 15:35:36

2010-01-21 11:27:30

linux多線程機(jī)制線程同步

2012-06-05 02:12:55

Java多線程

2023-04-26 16:34:12

2021-10-21 08:13:11

Springboot

2015-07-22 09:39:38

IOS多線程同步

2021-01-13 11:03:20

Python數(shù)據(jù)代碼
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號

大乳护士喂奶hd| 久久免费视频2| 久久久久久少妇| 欧美日韩播放| 欧美私模裸体表演在线观看| 亚洲午夜精品一区二区| www.国产三级| 美女精品一区| 日韩在线观看免费高清| 国产av一区二区三区传媒| 亚洲同志男男gay1069网站| 国产精品久久久久久久蜜臀| 成人91视频| 特级西西444www高清大视频| 国内揄拍国内精品久久| 亚洲午夜未满十八勿入免费观看全集| 午夜一区二区视频| 国产亚洲成av人片在线观看| 国产精品成人免费精品自在线观看| 国产精品视频福利| 一区二区三区午夜| 欧美亚洲三级| 欧美极品美女视频网站在线观看免费| 日韩人妻无码精品综合区| 欧美精品影院| 精品视频色一区| 丰满人妻中伦妇伦精品app| 国产日产一区二区| 国产欧美日韩视频在线观看| 国产女主播一区二区| 亚洲天堂网视频| 久久亚洲精选| 91国产精品视频在线| 黄色一级片中国| 97精品国产福利一区二区三区| 亚洲经典中文字幕| 国产大学生av| 精品精品视频| 欧美日韩电影在线播放| 免费日韩视频在线观看| 成年男女免费视频网站不卡| 依依成人综合视频| 久久免费视频2| 三级外国片在线观看视频| 久久久久久久久岛国免费| 国产欧美日韩综合精品二区| 亚洲av无码片一区二区三区| 韩国精品久久久| 国产精品久久久久秋霞鲁丝| 狠狠人妻久久久久久| 亚洲精品日韩久久| 色中色综合影院手机版在线观看| 亚洲区一区二区三| 日韩www.| 日韩网站免费观看| 性爱在线免费视频| 日本精品黄色| 国产一区二区动漫| 法国空姐电影在线观看| 九色成人国产蝌蚪91| 亚洲老头同性xxxxx| 亚洲精品在线视频免费观看| 国产精品22p| 亚洲国产精品成人va在线观看| 无码人妻一区二区三区精品视频| 亚洲日本一区二区三区在线| 精品日本一线二线三线不卡| 久久久无码人妻精品无码| 成人在线视频中文字幕| 亚洲精品国产精品乱码不99按摩| 日本japanese极品少妇| 亚州国产精品| 一色桃子一区二区| 九九这里只有精品视频| 亚洲免费二区| 欧美精品videossex性护士| 久久久91视频| 国内精品嫩模av私拍在线观看| 欧美精品激情在线观看| 六月丁香在线视频| 日韩在线观看一区二区| 国产在线日韩在线| 不卡的日韩av| 久久亚洲二区三区| 日韩av电影在线观看| 香蕉视频在线免费看| 亚洲精品欧美激情| 日韩av新片网| 亚洲综合av一区二区三区| 欧美妇女性影城| 亚洲免费观看在线| 中文字幕中文字幕精品| 正在播放亚洲1区| 国产黄色片在线免费观看| 亚洲免费播放| 国产欧美日韩视频| 亚洲高清在线观看视频| 久久久精品2019中文字幕之3| 亚洲免费精品视频| 18video性欧美19sex高清| 色偷偷成人一区二区三区91 | 国产精品毛片va一区二区三区| 人妻少妇一区二区三区| 国产日本亚洲高清| 欧美a级免费视频| 亚洲天堂1区| 亚洲精品一区二区三区99| 亚洲第一综合网| 欧美网站在线| 日韩av日韩在线观看| 精品国产一级片| 中文字幕成人在线观看| 日韩av新片网| 美女久久精品| 亚洲网站视频福利| 精品一级少妇久久久久久久| 蜜桃在线一区二区三区| 国产综合av一区二区三区| 日本视频在线播放| 在线一区二区视频| 亚洲制服丝袜在线播放| 68国产成人综合久久精品| 日本久久亚洲电影| 免费看黄网站在线观看| 亚洲欧美影音先锋| 毛葺葺老太做受视频| 高潮久久久久久久久久久久久久 | 色噜噜日韩精品欧美一区二区| 亚洲一区欧美| 国产精品一区二区三| 欧美日韩国产中文字幕在线| 亚洲午夜激情网站| 色姑娘综合天天| 久久久久久久久久久妇女| 国产精品成人一区| 丝袜+亚洲+另类+欧美+变态| 亚洲自拍偷拍网站| 波多野结衣三级视频| 天天天综合网| 成人www视频在线观看| 国产成人天天5g影院在线观看 | 小泽玛利亚一区二区三区视频| 99re这里都是精品| 国产妇女馒头高清泬20p多| 榴莲视频成人app| 美女视频黄免费的亚洲男人天堂| 亚洲字幕av一区二区三区四区| 国产欧美综合在线观看第十页| 亚洲中文字幕无码中文字| 国偷自产av一区二区三区| 久久久久久久国产精品| 亚洲xxxx天美| 亚洲影视资源网| 成年女人免费视频| 影音先锋久久久| 精品欧美日韩在线| 小视频免费在线观看| 亚洲乱码一区二区| 国产免费一区二区三区四区五区| 久久综合狠狠综合久久激情| 青青草视频在线免费播放| 秋霞在线一区| 欧美中文字幕在线观看| 国产中文字幕在线视频| 欧美主播一区二区三区| 国产精品1区2区3区4区| 国内国产精品久久| 青青视频免费在线观看| 极品国产人妖chinesets亚洲人妖 激情亚洲另类图片区小说区 | 五月天婷婷在线播放| 懂色av一区二区三区| 国产中年熟女高潮大集合| 日本 国产 欧美色综合| 国产日产欧美一区二区| 中文一区二区三区四区| 91爱视频在线| 成人av一区| 欧美疯狂做受xxxx富婆| 欧美久久久久久久久久久久| 99久久综合99久久综合网站| 日韩欧美黄色大片| 91tv官网精品成人亚洲| 国产一区二区三区四区hd | 成人黄色网免费| 免费电影网站在线视频观看福利| 亚洲精品久久久一区二区三区| 久久精品视频5| 中文字幕一区二区三中文字幕| av电影中文字幕| 老牛嫩草一区二区三区日本| 亚洲一区三区电影在线观看| 国产精品x8x8一区二区| 国产精品久久中文| 视频在线观看入口黄最新永久免费国产 | 成人中文字幕在线播放| 欧美一区二区三区激情视频| 91久久精品一区二区别| 中文字幕在线视频久| 精品国偷自产在线| 亚洲欧洲综合在线| 欧美精品在线视频| 国产精品人人人人| 亚洲男同性恋视频| 国产sm调教视频| 夫妻av一区二区| 久久黄色片网站| 亚洲精品影视| 日韩国产精品毛片| 国产精品亚洲片在线播放| 91精品免费| 日本成人片在线| 91精品国产高清久久久久久| 免费在线观看黄色| 亚洲视频一区二区| 成人久久精品人妻一区二区三区| 欧美午夜精品电影| 香蕉免费毛片视频| 亚洲精品伦理在线| 欧美巨胸大乳hitomi| 97久久久精品综合88久久| 精品国产午夜福利在线观看| 男女激情视频一区| 欧美色图色综合| 影音先锋在线一区| 操bbb操bbb| 99久久久久国产精品| 欧美精品国产精品久久久| 盗摄牛牛av影视一区二区| 91黄在线观看| 国产情侣一区在线| 国产精品综合久久久| 欧美成人资源| 欧美亚洲成人精品| 超碰成人av| 久久久久久高潮国产精品视| 污污网站在线看| 日韩视频永久免费观看| 69xxxx欧美| 中文字幕在线看视频国产欧美在线看完整| 内衣办公室在线| 亚洲精选中文字幕| 人成在线免费视频| 日韩精品视频三区| 亚洲色图欧美视频| 欧美一区二区三区在线观看视频| 国产精品一区二区黑人巨大| 欧美日本国产一区| 国产又粗又猛视频免费| 欧美日韩一区二区在线视频| 一级爱爱免费视频| 欧美高清激情brazzers| 91在线你懂的| 538prom精品视频线放| 国产精品人妻一区二区三区| 欧美一区二区黄色| 国产富婆一级全黄大片| 欧美成人三级在线| 亚洲av无码一区二区乱子伦| 亚洲国产高清自拍| 日韩精品一二| 影音先锋日韩有码| 欧美三级电影一区二区三区| 久久国产精品亚洲| 欧美性受ⅹ╳╳╳黑人a性爽| 欧美激情乱人伦| 免费h视频在线观看| 欧美亚洲在线观看| 中韩乱幕日产无线码一区| 成人黄色免费看| 91亚洲无吗| 欧美高清视频一区| 99精品电影| 国产在线观看欧美| 国产一区二区三区久久| xxxx一级片| 国产精品888| 中文字幕免费高清视频| 国产欧美一区二区三区沐欲 | 五月婷婷开心中文字幕| 亚洲欧美变态国产另类| 免费av网站在线观看| 欧美极品欧美精品欧美视频| 经典三级一区二区| 91网站免费看| 蜜桃av鲁一鲁一鲁一鲁俄罗斯的 | 国产精品精品视频| 亚洲成人1区| 国产精品对白一区二区三区| 亚洲国产欧美日韩在线观看第一区| 五月天综合网| 国产精品av久久久久久麻豆网| 国产肥臀一区二区福利视频| 麻豆国产精品视频| 美女网站视频在线观看| 国产三级一区二区| 九九热国产精品视频| 91高清视频在线| 亚洲第一视频在线| 揄拍成人国产精品视频| 欧洲在线视频| 国产日韩精品综合网站| 加勒比视频一区| 中文字幕在线亚洲精品| 国产美女精品| 先锋资源在线视频| 国产欧美一区二区在线| 国产一级在线播放| 欧美区在线观看| 欧美孕妇孕交xxⅹ孕妇交| 久99久在线视频| 国产亚洲精彩久久| 精品国产一区二区三区日日嗨| 91麻豆精品国产91久久久平台 | 免费观看久久久4p| 亚州av综合色区无码一区| 椎名由奈av一区二区三区| av网站中文字幕| 亚洲第一国产精品| 成人在线播放免费观看| 国产精品一区二区久久久| 西野翔中文久久精品字幕| 无码熟妇人妻av在线电影| 久久精品99久久久| 国产伦理片在线观看| 亚洲观看高清完整版在线观看| 国产又粗又猛又黄| 中文字幕日韩欧美精品在线观看| 竹内纱里奈兽皇系列在线观看| 99re国产在线播放| 911久久香蕉国产线看观看| 9l视频白拍9色9l视频| 免费欧美日韩国产三级电影| 精品人妻伦一二三区久| 亚洲欧美色一区| 一级特黄录像免费看| 亚洲亚裔videos黑人hd| 九色porny丨入口在线| 福利视频久久| 在线高清一区| 国产ts在线观看| 亚洲综合色视频| 午夜精品无码一区二区三区| 不用播放器成人网| 4438五月综合| 一区视频二区视频| 久久99国产精品免费| 三级黄色免费观看| 欧美日韩成人综合天天影院| sese在线视频| 国产自摸综合网| 亚洲综合色站| 制服下的诱惑暮生| 亚洲最色的网站| 亚洲国产av一区二区| 欧美日韩高清在线观看| 91蝌蚪精品视频| 日本中文字幕网址| 久久久综合视频| av首页在线观看| 色婷婷久久一区二区| 亚洲欧美专区| 天天在线免费视频| 成人一二三区视频| 日韩欧美国产亚洲| 亚洲欧美日韩国产精品| 蜜桃视频成人m3u8| 中文字幕免费在线不卡| 国产一区视频导航| 久视频在线观看| 亚洲免费视频观看| www.久久.com| 日本特级黄色大片| 国产成人综合亚洲91猫咪| 日韩aaaaaa| 在线亚洲男人天堂| 日本高清精品| 欧美不卡在线播放| 中文字幕精品一区二区三区精品| 亚洲一区二区三区网站| 欧美日韩福利视频| 久久成人高清| 中文字幕在线视频一区二区三区| 亚洲一卡二卡三卡四卡| 蜜桃成人在线视频| 国产自摸综合网| 国产麻豆综合| 免费成人美女女在线观看| 精品蜜桃在线看| 国产精品亚洲一区二区三区在线观看 | 欧美激情a∨在线视频播放| 色婷婷综合久久久久久| 四季av一区二区三区| 午夜成人在线视频| avtt亚洲| 久久www免费人成精品| 蜜臀91精品一区二区三区| 国产精品第九页| 在线亚洲午夜片av大片| 国产精品1luya在线播放| 最新天堂中文在线|