juc-1-locksupport

1-locksupport

  • 在java中,当需要阻塞或者唤醒一个线程时,都会使用LockSupport工具类来完成相应的工作。LockSupport类中定义了一组公共静态方法,这些方法提供了最基本的线程阻塞和唤醒功能。

  • 本文基于jdk11的源码来进行解析。

  • 方法列表

    • park()阻塞当前线程

      • park()阻塞
        park(Object blocker)阻塞
        parkUntil(long deadline)阻塞直到deadline
        parkUntil(Object blocker,long deadline)阻塞直到deadline
        parkNanos(long nanos)阻塞nanos毫秒
        parkNanos(Object blocker,long nanos)阻塞nanos毫秒
    • unpark(Thread)解除阻塞的线程

      • unpark(Thread thread)解除指定线程的阻塞
    • blocker设置与获取

      • setBlocker(Thread t,Object blocker)设置指定线程的blocker
        getBlocker(Thread t)获取指定线程的blocker
  • 用法

    • 解除阻塞的时机

      • 1.其他线程调用unpark指定接触此线程的阻塞
      • 2.parkNanos或parkUntil超时了
      • 3.此线程被其他线程中断了
      • 4.无原因的返回
      • 因此需要注意到底是可以获取资源了(其他线程调用unpark)还是超时或其他原因返回了,所以一般需要循环判断在不可获得资源情况下重新进入park状态。
    • 先unpark后park

      • 如果先对此线程调用了unpark,之后此线程在调用park时不会阻塞,会直接返回。
  • 工作原理

    • park及带时间的park

      • LockSupport类中的park方法

        • private static final Unsafe U = Unsafe.getUnsafe();
          public static void park() {
              U.park(false, 0L);
          }
          public static void parkNanos(long nanos) {
              if (nanos > 0)
                  U.park(false, nanos);
          }
          public static void parkUntil(long deadline) {
              U.park(true, deadline);
          }
      • park方法调用的Unsafe类代码

        • public native void park(boolean isAbsolute, long time);
      • Unsafe类中park对应的jvm源码

        • UNSAFE_ENTRY(void, Unsafe_Park(JNIEnv *env, jobject unsafe, jboolean isAbsolute, jlong time)) {
            HOTSPOT_THREAD_PARK_BEGIN((uintptr_t) thread->parker(), (int) isAbsolute, time);
            EventThreadPark event;
            JavaThreadParkedState jtps(thread, time != 0);
              //根据线程寻找parker
            thread->parker()->park(isAbsolute != 0, time);//真正的park实现
            if (event.should_commit()) {
              const oop obj = thread->current_park_blocker();
              if (time == 0) {
                post_thread_park_event(&event, obj, min_jlong, min_jlong);
              } else {
                if (isAbsolute != 0) {
                  post_thread_park_event(&event, obj, min_jlong, time);
                } else {
                  post_thread_park_event(&event, obj, time, min_jlong);
                }
              }
            }
            HOTSPOT_THREAD_PARK_END((uintptr_t) thread->parker());
          } UNSAFE_END
          
      • thread->parker()->park(isAbsolute != 0, time);中park的源码

        • void Parker::park(bool isAbsolute, jlong time) {
            if (Atomic::xchg(0, &_counter) > 0) return;//设置成0失败则直接返回不阻塞
            Thread* thread = Thread::current();
            JavaThread *jt = (JavaThread *)thread;
            if (Thread::is_interrupted(thread, false)) {//如果此时被中断,直接返回不阻塞
              return;
            }
            struct timespec absTime;
            if (time < 0 || (isAbsolute && time == 0)) { //time设置错误或无需阻塞
              return;
            }
            if (time > 0) {
              to_abstime(&absTime, time, isAbsolute);//获取真正阻塞时间
            }
            ThreadBlockInVM tbivm(jt);
            if (Thread::is_interrupted(thread, false) ||
                pthread_mutex_trylock(_mutex) != 0) {//中断了或加锁失败直接返回不阻塞
              return;
            }
          
            int status;
            if (_counter > 0)  { //counter>0直接返回不阻塞
              _counter = 0;//并将counter设置回0
              status = pthread_mutex_unlock(_mutex);//解锁
              OrderAccess::fence();
              return;
            }
            OSThreadWaitState osts(thread->osthread(), false);
            jt->set_suspend_equivalent();
            if (time == 0) {//时间=0则为park()
              _cur_index = REL_INDEX;
              /***********************************
              	调用pthread_cond_wait来等待唤醒
              ***********************************/
              status = pthread_cond_wait(&_cond[_cur_index], _mutex);
              assert_status(status == 0, status, "cond_timedwait");
            }
            else {//有阻塞时间限制的parknanos/parkuntil
              _cur_index = isAbsolute ? ABS_INDEX : REL_INDEX;
              /***********************************
             		调用pthread_cond_timedwait来等待唤醒
              ***********************************/
              status = pthread_cond_timedwait(&_cond[_cur_index], _mutex, &absTime);
              assert_status(status == 0 || status == ETIMEDOUT,
                            status, "cond_timedwait");
            }
            _cur_index = -1;
            _counter = 0;
            status = pthread_mutex_unlock(_mutex);//解锁
            //***********
          }
          
          • 通过代码可以发现park的阻塞的底层实现是通过pthread的中的pthread_cond_wait以及pthread_cond_timedwait来实现的。
          • park为pthread_cond_wait,parkNanos和parkUntil是pthread_cond_timedwait。
    • unpark

      • LockSupport类中的unpark方法

        • private static final Unsafe U = Unsafe.getUnsafe();
          public static void unpark(Thread thread) {
              if (thread != null)
                  U.unpark(thread);
          }
      • unpark方法调用的Unsafe类代码

        • public native void unpark(Object thread);
      • Unsafe类中unpark对应的jvm源码

        • UNSAFE_ENTRY(void, Unsafe_Unpark(JNIEnv *env, jobject unsafe, jobject jthread)) {
            Parker* p = NULL;
          
            if (jthread != NULL) {
              ThreadsListHandle tlh;
              JavaThread* thr = NULL;
              oop java_thread = NULL;
              (void) tlh.cv_internal_thread_to_JavaThread(jthread, &thr, &java_thread);
              if (java_thread != NULL) {//java线程有效
                //获取java中parker的地址
                jlong lp = java_lang_Thread::park_event(java_thread);
                if (lp != 0) {//java中是否设置了parker
                  p = (Parker*)addr_from_java(lp);//找寻parker
                } else {//没设置
                  if (thr != NULL) {
                    p = thr->parker();//找寻parker
                    if (p != NULL) {
                      java_lang_Thread::set_park_event(java_thread, addr_to_java(p));
                    }
                  }
                }
              }
            }
            if (p != NULL) {
              HOTSPOT_THREAD_UNPARK((uintptr_t) p);
              /***********************************
             		真正的unpark调用
              ***********************************/
              p->unpark();
            }
          } UNSAFE_END
          
      • p->unpark()中unpark的源码

        • void Parker::unpark() {
            int status = pthread_mutex_lock(_mutex);//加锁
            const int s = _counter;
            _counter = 1;//设置counter为1
            int index = _cur_index;
            status = pthread_mutex_unlock(_mutex);//解锁
            if (s < 1 && index != -1) {
             /***********************************
             		调用pthread_cond_signal来唤醒此线程
              ***********************************/   
              status = pthread_cond_signal(&_cond[index]);
            }
          }
          
        • 因为unpark传入了需要唤醒的指定线程,而通过此线程获取到了这个线程的parker来通过pthread_cond_signal唤醒。

    • park/unpark总结

      • _counter
        • 每一个线程对应的Parker中有一个counter变量
          • 不管进行多少次unpark,counter都为1
          • park时若counter为1则直接返回不阻塞
          • park返回时会将counter设置回0
      • pthread中mutex和condition的使用
        • 在park过程中使用mutex来加锁设置counter的值。
        • 使用condition来进行线程的阻塞、带超时时间的阻塞与唤醒。
    • pthread方法

      • mutex
        • 在park和unpark中使用了 pthread_mutex_trylock、pthread_mutex_lock加锁与pthread_mutex_unlock解锁。

        • pthread_mutex_init初始化一个mutex
          pthread_mutex_lock加锁(阻塞直到加索成功)
          pthread_mutex_trylock尝试一次加索(不阻塞)
          pthread_mutex_unlock解锁
      • conditiion
        • 在park系列方法中调用了pthread_cond_wait以及pthread_cond_timedwait,unpark调用了pthread_cond_signal。

        • int pthread_cond_init(pthread_cond_t *cond,pthread_condattr_t *cond_attr)创建一个condition的内核变量
          int pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex)等待内核变量变为signaled
          int pthread_cond_timewait(pthread_cond_t *cond,pthread_mutex *mutex,const timespec *abstime)在指定时间内等待内核变量变为signaled
          int pthread_cond_signal(pthread_cond_t *cond)把内核变量置为signaled,随机激活一个等待的线程
        • 一个线程有一个Parker类(C++)实例,一个park实例自己初始化自己的condition内核变量,因此在调用pthread_cond_signal时可以唤醒唯一的线程,因为一个condition对应一个线程。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值