是使用Thread.sleep(200) 还是使用 LockSupport.parkNanos(200*1000*1000l) ?

相信这个问题很少人会想过问这个问题,但是看到相关文章后定然是新奇。

我相信有很多人都知道LockSupport 这个工具类,但绝大多数人学JUC都只是处于应用JUC。没有对底层原理思考。

那么在我提出这个问题的时候,你能说出其中的一二吗?

Thread.sleepLockSupport.parkNanos 两者现象都能让线程暂停下来,但是底层的原理有所区别。

在java类库中2个方法都标有相应的注释,注释中解释到: sleep 虽然让线程暂停了,但是不会释放资源,而LockSupport.park会释放资源。

在看完部分jdk底层代码后,大致上可以看出一些内容,实际上2者在jdk中都由JavaThread对象进行调用。


一、LockSupport对于Park的实现

LockSupport是由Unsafe实现的对应openJDK源码位置 src/hotspot/share/prims/unsafe.cpp

parkNanos 调用 park然后 最终调用是

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);
  thread->parker()->park(isAbsolute != 0, time);
  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

这里是上面调用的那个方法,在源码中就在Unsafe_Park上面

static void post_thread_park_event(EventThreadPark* event, const oop obj, jlong timeout_nanos, jlong until_epoch_millis) {
  assert(event != NULL, "invariant");
  assert(event->should_commit(), "invariant");
  event->set_parkedClass((obj != NULL) ? obj->klass() : NULL);
  event->set_timeout(timeout_nanos);
  event->set_until(until_epoch_millis);
  event->set_address((obj != NULL) ? (u8)cast_from_oop<uintptr_t>(obj) : 0);
  event->commit();
}

post_thread_park_event 从名称就可以大致看出它的实现是调用JavaThreadPrakEvent实现(与下面的Thread.sleep)一致

二、Thread对于sleep的实现

需要一点耐心,看下注释和代码的命名,见名知意。
大致可以看出 sleep是一个自旋+park实现
源码位置:src/hotspot/share/runtime/thread.cpp

// java.lang.Thread.sleep support
// Returns true if sleep time elapsed as expected, and false
// if the thread was interrupted.
bool JavaThread::sleep(jlong millis) {
  assert(this == Thread::current(),  "thread consistency check");

  ParkEvent * const slp = this->_SleepEvent;
  // Because there can be races with thread interruption sending an unpark()
  // to the event, we explicitly reset it here to avoid an immediate return.
  // The actual interrupt state will be checked before we park().
  slp->reset();
  // Thread interruption establishes a happens-before ordering in the
  // Java Memory Model, so we need to ensure we synchronize with the
  // interrupt state.
  OrderAccess::fence();

  jlong prevtime = os::javaTimeNanos();

  for (;;) {
    // interruption has precedence over timing out
    if (this->is_interrupted(true)) {
      return false;
    }

    if (millis <= 0) {
      return true;
    }

    {
      ThreadBlockInVM tbivm(this);
      OSThreadWaitState osts(this->osthread(), false /* not Object.wait() */);

      this->set_suspend_equivalent();
      // cleared by handle_special_suspend_equivalent_condition() or
      // java_suspend_self() via check_and_wait_while_suspended()

      slp->park(millis);

      // were we externally suspended while we were waiting?
      this->check_and_wait_while_suspended();
    }

    // Update elapsed time tracking
    jlong newtime = os::javaTimeNanos();
    if (newtime - prevtime < 0) {
      // time moving backwards, should only happen if no monotonic clock
      // not a guarantee() because JVM should not abort on kernel/glibc bugs
      assert(!os::supports_monotonic_clock(),
             "unexpected time moving backwards detected in JavaThread::sleep()");
    } else {
      millis -= (newtime - prevtime) / NANOSECS_PER_MILLISEC;
    }
    prevtime = newtime;
  }
}

看完2者的实现后感觉sleep有占用cpu时间片的情况(自旋更新park剩余时间)只能被中断休眠,否则会一直自旋直到休眠结束从cpp源码中也可以看出,中断后就会return false结束自旋
对于LockSupport 看起来效率更高,但存在虚假唤醒的可能(当然park的线程是可以被unpark唤醒的)
当然如果我们只是想要让线程暂停一会的话,在不考虑虚假唤醒的情况下,使用LockSupport的效率更高。

虚假唤醒

实际上虚假唤醒并非不可控,这里面的"虚假"一词并非说一个值为0的数突然变成了1,实际上"虚假"是人为造成的,而之所以产生这一词是因为Object.notifyAll()会唤醒所有线程,有些线程我们并不像唤醒它,但是因为与Object.notifyAll()类似,导致出现本不该唤醒的线程被唤醒了。故出现了虚假唤醒现象,实际上在我们平时写代码的过程中,如果我们对于自己的代码逻辑非常确定不存在虚假唤醒的线程,那么大可以放心的使用LockSupport.parkNanosLockSupport.parkUntilObject.notifyAll()等存在虚假唤醒的代码。

测试代码

为了更好的理解建议运行一下下面的代码,可以看下效果

import java.time.Instant;
import java.util.concurrent.locks.LockSupport;

public class Demo {

    private final static Thread sleepThread =new Thread(()->{
        try {
            Thread.sleep(20*1000);// 休眠20秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("线程从sleep被唤醒");
    });
    private final static Thread parkThread =new Thread(()->{
        LockSupport.parkUntil(Instant.now().toEpochMilli()+20000);//休眠10秒
        System.out.println("线程从park状态中被唤醒");
    });


    public static void main(String[] args) {
//        sleepThread.start();
        parkThread.start();
        new Thread(()->{
            System.out.println("3秒后唤醒休眠线程");

            LockSupport.parkUntil(Instant.now().toEpochMilli()+3000);// 休眠3秒
            System.out.println("开始唤醒线程");
            LockSupport.unpark(parkThread);
//            try {
//                sleepThread.interrupt();
//            } catch (Exception e) {
//                e.printStackTrace();
//            }
        }).start();
    }
}

其实作者我只是想告诉大家,平时除了写Thread.sleep(200)
还可以使用LockSupport.parkUntil(Instant.now().toEpochMilli()+3000);// 休眠3秒 java8新的时间类
还可以使用LockSupport.parkNanos(3*1000*1000*1000l) ;// 休眠3秒
来实现线程的停顿

at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:88) at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:59) at kotlinx.coroutines.BuildersKt.runBlocking(unavailable:1) at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:38) at kotlinx.coroutines.BuildersKt.runBlocking$default(unavailable:1) at com.oplus.utrace.sdk.internal.a.b(unavailable:0) at com.oplus.utrace.sdk.internal.a.g(unavailable:0) - locked <@addr=0x15b594e8> (a java.lang.String[]) at com.oplus.utrace.sdk.internal.a.c(unavailable:0) at com.oplus.utrace.sdk.UTrace.a(unavailable:0) at com.oplus.utrace.sdk.UTrace.startHead(unavailable:0) at com.oplus.utrace.sdk.UTrace.startHead$default(unavailable:0) at com.oplus.pantanal.seedling.f.c.a(unavailable:0) at com.oplus.pantanal.seedling.f.b.a(unavailable:0) at com.oplus.pantanal.seedling.intent.a.sendSeedling(unavailable:0) at com.oplus.pantanal.seedling.util.SeedlingTool.sendSeedling(unavailable:0) at com.oplus.pantanal.seedling.intent.IIntentManager$DefaultImpls.sendSeedling$default(unavailable:0) at com.oplus.systemui.seedlingservice.tip.NormalTip.cancelIntent(NormalTip.kt:167) at com.oplus.systemui.seedlingservice.flashlight.FlashlightTip.onLocked(FlashlightTip.kt:230) at com.oplus.systemui.seedlingservice.statemanager.StateManager$screenStateReceiver$1.onReceive(StateManager.kt:39) at android.app.LoadedApk$ReceiverDispatcher$Args.lambda$getRunnable$0(LoadedApk.java:1960) at android.app.LoadedApk$ReceiverDispatcher$Args.$r8$lambda$mcNAAl1SQ4MyJPyDg8TJ2x2h0Rk(unavailable:0) at android.app.LoadedApk$ReceiverDispatcher$Args$$ExternalSyntheticLambda0.run(D8$$SyntheticClass:0) at android.os.Handler.handleCallback(Handler.java:959) at android.os.Handler.dispatchMessage(Handler.java:100) at android.os.Looper.loopOnce(Looper.java:282) at android.os.Looper.loop(Looper.java:387) at a
最新发布
04-04
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

诗水人间

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值