LockSupport

 

 
concurrent包是基于AQS (AbstractQueuedSynchronizer)框架的,AQS框架借助于两个类:
  • Unsafe(提供CAS操作)
  • LockSupport(提供park/unpark操作,底层仍然调用是Unsafe类的park/unpark方法)
因此,LockSupport非常重要。先看一下park和unpark方法怎么用的吧。
 
示例:
public static void main(String[] args) throws Exception {

    Thread thread = new Thread(() -> {
        System.out.println("开始睡眠...");
        // 开始睡眠
        LockSupport.park();
        System.out.println("已被唤醒!");
    });
    thread.start();
    Thread.sleep(1000);
    System.out.println("睡了1秒钟之后开始唤醒...");
    // 开始唤醒
    LockSupport.unpark(thread);
}

输出:

开始睡眠...
睡了1秒钟之后开始唤醒...
已被唤醒!

从示例中,我们大概就能明白park和unpark方法的作用,park方法类似于wait,unpark方法类似于notify。下面我们再看一下复杂一点的示例,来深刻理解park和unpark方法的作用。

 
 
二、底层原理
 
LockSupport类park方法和unpark方法
public static void park() {
    UNSAFE.park(false, 0L);
}
// ...   
public static void unpark(Thread thread) {
    if (thread != null)
        UNSAFE.unpark(thread);
}

 

Unsafe.park和Unsafe.unpark的底层实现原理
public native void park(boolean isAbsolute, long time);
public native void unpark(Object var1);

  

每个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来实现的。mutex是操作系统函数,Synchronized底层也是调用mutex互斥锁的。

在Parker类里的_counter字段,就是用来记录“许可”的。
 
 
park过程
当调用park时,先尝试能否直接拿到“许可”,即_counter>0时,如果成功,则把_counter设置为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;  

  

如果不成功,则构造一个ThreadBlockInVM,然后检查_counter是不是>0,如果是,则把_counter设置为0,unlock <wiz_tmp_highlight_tag class="cm-searching">mutex并返回:
ThreadBlockInVM tbivm(jt);  
if (_counter > 0)  { // no wait needed  
  _counter = 0;  
  status = pthread_mutex_unlock(_mutex);  

  

否则,再判断等待的时间,然后再调用pthread_cond_wait函数等待,如果等待返回,则把_counter设置为0,unlock <wiz_tmp_highlight_tag class="cm-searching">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过程
当unpark时,则简单多了,直接设置_counter为1,再unlock <wiz_tmp_highlight_tag class="cm-searching">mutex返回。如果_counter之前的值是0,则还要调用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") ;  
  }  
}  

  

 

posted @ 2020-06-16 10:31  cao_xiaobo  阅读(174)  评论(0编辑  收藏  举报