Java线程源码解析之yield和sleep

18、sleep()、wait()、join()、yield()的区别 sleep()、wait()、join()、yield()的区别 阅读详情

概述

由于Thread的yield和sleep有一定的相似性,因此放在一起进行分析。yield会释放CPU资源,让优先级更高(至少是相同)的线程获得执行机会;sleep当传入参数为0时,和yield相同;当传入参数大于0时,也是释放CPU资源,当可以让其它任何优先级的线程获得执行机会;

假设当前进程只有main线程,当调用yield之后,main线程会继续运行,因为没有比它优先级更高的线程;而调用sleep之后,mian线程会进入TIMED_WAITING状态,不会继续运行;

yield

Thread.sleep底层是通过JVM_Yield方法实现的(见jvm.cpp):

JVM_ENTRY(void, JVM_Yield(JNIEnv *env, jclass threadClass))
  JVMWrapper("JVM_Yield");
  //检查是否设置了DontYieldALot参数,默认为fasle
  //如果设置为true,直接返回
  if (os::dont_yield()) return;
 //如果ConvertYieldToSleep=true(默认为false),调用os::sleep,否则调用os::yield
  if (ConvertYieldToSleep) {
    os::sleep(thread, MinSleepInterval, false);//sleep 1ms
  } else {
    os::yield();
  }
JVM_END

从上面知道,实际上调用的是os::yield:

//sched_yield是linux kernel提供的API,它会使调用线程放弃CPU使用权,加入到同等优先级队列的末尾;
//如果调用线程是优先级最高的唯一线程,yield方法返回后,调用线程会继续运行;
//因此可以知道,对于和调用线程相同或更高优先级的线程来说,yield方法会给予了它们一次运行的机会;
void os::yield() {
  sched_yield();
}

sleep

Thread.sleep最终调用JVM_Sleep方法:

JVM_ENTRY(void, JVM_Sleep(JNIEnv* env, jclass threadClass, jlong millis))
  JVMWrapper("JVM_Sleep");

  if (millis < 0) {//参数校验
    THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "timeout value is negative");
  }

  //如果线程已经中断,抛出中断异常,关于中断的实现,在另一篇文章中会讲解
  if (Thread::is_interrupted (THREAD, true) && !HAS_PENDING_EXCEPTION) {
    THROW_MSG(vmSymbols::java_lang_InterruptedException(), "sleep interrupted");
  }
 //设置线程状态为SLEEPING
  JavaThreadSleepState jtss(thread);

  EventThreadSleep event;

  if (millis == 0) {
    //如果设置了ConvertSleepToYield(默认为true),和yield效果相同
    if (ConvertSleepToYield) {
      os::yield();
    } else {//否则调用os::sleep方法
      ThreadState old_state = thread->osthread()->get_state();
      thread->osthread()->set_state(SLEEPING);
      os::sleep(thread, MinSleepInterval, false);//sleep 1ms
      thread->osthread()->set_state(old_state);
    }
  } else {//参数大于0
   //保存初始状态,返回时恢复原状态
    ThreadState old_state = thread->osthread()->get_state();
    //osthread->thread status mapping:
    // NEW->NEW
    //RUNNABLE->RUNNABLE
    //BLOCKED_ON_MONITOR_ENTER->BLOCKED
    //IN_OBJECT_WAIT,PARKED->WAITING
    //SLEEPING,IN_OBJECT_WAIT_TIMED,PARKED_TIMED->TIMED_WAITING
    //TERMINATED->TERMINATED
    thread->osthread()->set_state(SLEEPING);
    //调用os::sleep方法,如果发生中断,抛出异常
    if (os::sleep(thread, millis, true) == OS_INTRPT) {
      if (!HAS_PENDING_EXCEPTION) {
        if (event.should_commit()) {
          event.set_time(millis);
          event.commit();
        }
        THROW_MSG(vmSymbols::java_lang_InterruptedException(), "sleep interrupted");
      }
    }
    thread->osthread()->set_state(old_state);//恢复osThread状态
  }
  if (event.should_commit()) {
    event.set_time(millis);
    event.commit();
  }
JVM_END

os::sleep的源码如下:

int os::sleep(Thread* thread, jlong millis, bool interruptible) {
  assert(thread == Thread::current(),  "thread consistency check");
  //线程有如下几个成员变量:
  //ParkEvent * _ParkEvent ;          // for synchronized()
  //ParkEvent * _SleepEvent ;        // for Thread.sleep
  //ParkEvent * _MutexEvent ;      // for native internal Mutex/Monitor
  //ParkEvent * _MuxEvent ;         // for low-level muxAcquire-muxRelease
  ParkEvent * const slp = thread->_SleepEvent ;
  slp->reset() ;
  OrderAccess::fence() ;

//如果millis>0,传入interruptible=true,否则为false
  if (interruptible) {
    jlong prevtime = javaTimeNanos();

    for (;;) {
      if (os::is_interrupted(thread, true)) {//判断是否中断
        return OS_INTRPT;
      }

      jlong newtime = javaTimeNanos();//获取当前时间
      //如果linux不支持monotonic lock,有可能出现newtime<prevtime
      if (newtime - prevtime < 0) {
        assert(!Linux::supports_monotonic_clock(), "time moving backwards");
      } else {
        millis -= (newtime - prevtime) / NANOSECS_PER_MILLISEC;
      }

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

      prevtime = newtime;
      {
        assert(thread->is_Java_thread(), "sanity check");
        JavaThread *jt = (JavaThread *) thread;
        ThreadBlockInVM tbivm(jt);
        OSThreadWaitState osts(jt->osthread(), false );

        jt->set_suspend_equivalent();
        slp->park(millis);
        jt->check_and_wait_while_suspended();
      }
    }
  } else {//如果interruptible=false
   //设置osthread的状态为CONDVAR_WAIT
    OSThreadWaitState osts(thread->osthread(), false );
    jlong prevtime = javaTimeNanos();

    for (;;) {
      jlong newtime = javaTimeNanos();

      if (newtime - prevtime < 0) {
        assert(!Linux::supports_monotonic_clock(), "time moving backwards");
      } else {
        millis -= (newtime - prevtime) / NANOSECS_PER_MILLISEC;
      }

      if(millis <= 0) break ;

      prevtime = newtime;
      slp->park(millis);//底层调用pthread_cond_timedwait实现
    }
    return OS_OK ;
  }
}

通过阅读源码知道,原来sleep是通过pthread_cond_timedwait实现的,那么为什么不通过linux的sleep实现呢?

  • pthread_cond_timedwait既可以堵塞在某个条件变量上,也可以设置超时时间;
  • sleep不能及时唤醒线程,最小精度为秒;

可以看出pthread_cond_timedwait使用灵活,而且时间精度更高;

# 例子
通过strace可以查看代码的系统调用情况,建立两个类,一个调用Thread.sleep(),一个调用Thread.yield(),查看其系统调用情况:

  • Thread.sleep(0)
    Thread.sleep(0);
    System.out.println("hello");

    sleep0.png

    可以看到sched_yield的系统调用
  • Thread.sleep(nonzero)
    Thread.sleep(1000);
    System.out.println("hello");

sleep2.png


在其中并没有看到pthread_cond_timedwait的调用,其实Java的线程有可两种实现方式:

  1. LinuxThreads
  2. NPTL(Native POSIX Thread Library)
    // NPTL or LinuxThreads?
    static bool is_LinuxThreads()               { return !_is_NPTL; }
    static bool is_NPTL()                       { return _is_NPTL;  }
    可以通过如下命令查看到底是使用哪种线程实现:
    getconf GNU_LIBPTHREAD_VERSION

nptl.png

关于两者之间的区别,请查看wiki。由于我的机器上采用的是2,因此无法看到ppthread_cond_timedwait的调用;
ppthread_cond_timedwait采用futex(Fast Userspace muTEXes)实现,因而可以看到对futex的调用;

关于JVM是如何决定采用哪种实现方式,可以查看如下方法(os_linux.cpp):

// detecting pthread library

void os::Linux::libpthread_init() {
  // Save glibc and pthread version strings. Note that _CS_GNU_LIBC_VERSION
  // and _CS_GNU_LIBPTHREAD_VERSION are supported in glibc >= 2.3.2. Use a
  // generic name for earlier versions.
  // Define macros here so we can build HotSpot on old systems.
# ifndef _CS_GNU_LIBC_VERSION
# define _CS_GNU_LIBC_VERSION 2
# endif
# ifndef _CS_GNU_LIBPTHREAD_VERSION
# define _CS_GNU_LIBPTHREAD_VERSION 3
# endif

  size_t n = confstr(_CS_GNU_LIBC_VERSION, NULL, 0);
  if (n > 0) {
     char *str = (char *)malloc(n, mtInternal);
     confstr(_CS_GNU_LIBC_VERSION, str, n);
     os::Linux::set_glibc_version(str);
  } else {
     // _CS_GNU_LIBC_VERSION is not supported, try gnu_get_libc_version()
     static char _gnu_libc_version[32];
     jio_snprintf(_gnu_libc_version, sizeof(_gnu_libc_version),
              "glibc %s %s", gnu_get_libc_version(), gnu_get_libc_release());
     os::Linux::set_glibc_version(_gnu_libc_version);
  }
  //系统函数confstr获取C库信息
  n = confstr(_CS_GNU_LIBPTHREAD_VERSION, NULL, 0);
  if (n > 0) {
     char *str = (char *)malloc(n, mtInternal);
     confstr(_CS_GNU_LIBPTHREAD_VERSION, str, n);
     // Vanilla RH-9 (glibc 2.3.2) has a bug that confstr() always tells
     // us "NPTL-0.29" even we are running with LinuxThreads. Check if this
     // is the case. LinuxThreads has a hard limit on max number of threads.
     // So sysconf(_SC_THREAD_THREADS_MAX) will return a positive value.
     // On the other hand, NPTL does not have such a limit, sysconf()
     // will return -1 and errno is not changed. Check if it is really NPTL.
     if (strcmp(os::Linux::glibc_version(), "glibc 2.3.2") == 0 &&
         strstr(str, "NPTL") &&
         sysconf(_SC_THREAD_THREADS_MAX) > 0) {
       free(str);
       os::Linux::set_libpthread_version("linuxthreads");
     } else {
       os::Linux::set_libpthread_version(str);
     }
  } else {
    // glibc before 2.3.2 only has LinuxThreads.
    os::Linux::set_libpthread_version("linuxthreads");
  }

  if (strstr(libpthread_version(), "NPTL")) {
     os::Linux::set_is_NPTL();
  } else {
     os::Linux::set_is_LinuxThreads();
  }

  // LinuxThreads have two flavors: floating-stack mode, which allows variable
  // stack size; and fixed-stack mode. NPTL is always floating-stack.
  if (os::Linux::is_NPTL() || os::Linux::supports_variable_stack_size()) {
     os::Linux::set_is_floating_stack();
  }
}
  • Thread.yield
    Thread.yield();
    System.out.println("hello");

Paste_Image.png

和Thread.sleep(0)相同;

参考资料

  1. Linux 线程模型的比较:LinuxThreads 和 NPTL


作者:allanYan
链接:http://www.jianshu.com/p/0964124ae822
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
激光雷达与相机外参标定(附open3d python代码) 现在的激光雷达与相机的标定程序基本都是Ubuntu框架下面的,并且都是C++代码,需要安装的依赖也比较复杂,于是自己写了一个python版本的标定程序,依赖非常简单,Windows系统也可以运行。我这里用的大疆的雷达,点比较密集,更容易用我这种方法标定,旋转的16,32线的雷达也可以用这种方法标定,只是标定点会相对难选取一些。2 手动在点云上选取N个标定点(每个点都对应图像上的点,顺序也要一致)3 通过PNP方法计算出二者的旋转投影矩阵,也就是外参矩阵。1 手动在图像上面选取N个标定点。 阅读详情

相关推荐

CMOS 半加器和全加器&&数字集成电路&& Cadence Virtuoso

数字集成电路——半加器和全加器

计科小学生的博客 1万+

Java多线程六:线程优先级和yield()让步函数

可以看出,不管优先级和yield()函数都是试图控制线程的顺序执行.但是我们需要注意的一点是,在java中没有任何机制可以保证线程的执行顺序,两种做法只不过是增加了线程之间切换的几率而已.,

小鱼儿的专栏 4205

Vivado图形化界面烧写MCS文件到SPI FLASH全流程(附常见报错解决方案)

本文详细介绍了使用Vivado图形化界面将MCS文件烧录至SPI FLASH的完整操作流程。内容涵盖从环境准备、MCS文件生成到硬件连接与烧写的每一步,并针对SPI总线宽度报错、FLASH型号识别失败等常见问题提供了深度解决方案,帮助用户高效完成FPGA配置,摆脱对TCL命令行的依赖。

s8t9u0v1w的博客 535

Java多线程基础四 线程优先级之yield方法

yield单词是礼让的意思,是实现线程优先级的一种方式。线程调用yield会让出cpu的执行权,但别的线程不一定接受礼让。在cpu比较繁忙的时候效果明显,空闲的时候没什么影响。 下面的例子,有A B两个线程,不断对count累加,B执行yield方法,B线程的count变量累加次数明显减少。没有yield方法的话,A B两个线程的累加次数差不多。 /** * yield是礼让的意思,也就是让出线程cpu的执行权。 */ public class YieldDemo1 { public stati

ScottePerk的博客 1266

将CityScapes数据集转换为COCO格式的实例分割数据集

##¥##2019年结束前最后两小时,写出本年度最后一篇博客,不是博主没地方去跨年,实在是Coding乐趣无穷ahhhh ##¥## COCO数据集是研究实例分割的通用数据集,但是针对道路环境的数据较少,对于自动驾驶和移动机器人的应用并不适配。而提供道路环境的实例分割数据的数据集目前有KITTI、CityScapes、Apllo等,CityScapes算是应用较广,数据质量较高的一个,所以,本篇...

陆羽飞的博客 9940

银河麒麟服务器操作系统V10SP2实战:基于vsftpd构建多角色精细化权限FTP服务

本文详细介绍了在银河麒麟服务器操作系统V10SP2上基于vsftpd构建多角色精细化权限FTP服务的实战指南。从环境准备、核心配置到多角色用户权限管理,全面解析了FTP服务的部署与优化技巧,特别针对企业级应用场景提供了安全加固和故障排查方案。

weixin_30312563的博客 356

黑马程序员:Java基础——多线程的停止与守护以及Join,优先级和yield方法

------- Java EE培训、java培训、期待与您交流! ----------

JAVA程旭元的专栏 546

基于PyTorch的BP神经网络PID控制器动态调参实战

本文详细介绍了基于PyTorch的BP神经网络PID控制器动态调参实战,结合BP神经网络的自适应能力和PID控制算法的经典优势,实现工业控制中的高效参数调整。通过PyTorch实现BP-PID融合架构,包括网络设计、损失函数优化及动态调参技巧,显著提升控制性能,适用于温度控制、电机控制等场景。

weixin_29094775的博客 520

Java——线程让步_yield()方法

线程让步可以通过 yield ()方法来实现,该方法和 sleep ()方法有点相似,都可以让当前正在运行的线程暂停,区别在于 yield() 方法不会阻塞该线程,它只是将线程转换成就绪状态,让系统的调度器重新调度一次。当某个线程调用 yield ()方法之后,只有与当前线程优先级相同或者更高的线程才能获得执行的机会。 ExampleM09: //线程让步 //定义 YieldThread 类继承 Thread 类 class YieldThread extends Thread { //定义一个有

XQC_KKK的博客 879

PCL CSF布料模拟算法【2026最新版】

CSF布料模拟地面滤波算法的PCL实现,博客长期更新,本文最近更新时间为:2026年10月26日。

点云侠的博客 3916

【制作100个unity游戏之22】3DRPG游戏开发07——其他详细的设计

我们会有有很多单例,如果每个单例都去写会很麻烦,所以我们定义泛型单例模式 Singleton,我们将所有的单例都继承这个泛型单例,就省略很多重复的工作定义一个GameManager类,继承Singleton,控制整个游戏的进度,比如如果主角死了,广播,游戏结束调用。

向宇的博客,专注php/web全栈 unity游戏开发,欢迎大家评论纠错 2576

Java线程调度以及算法

Java的线程调度程序是JVM的一部分,它决定应该运行哪个线程。无法保证线程调度程序将选择运行哪个可运行线程。一次只能有一个线程在一个进程中运行。线程调度程序主要使用抢占式或时间切片调度来调度线程。在抢占式调度下,优先级最高的任务一直执行,直到它进入等待或死亡状态或更高优先级的任务出现。在时间切片下,任务执行预定义的一段时间,然后重新进入就绪任务池。然后,调度程序根据优先级和其他因素确定接下来应执行的任务。

weixin_62458944的博客 610

qt实现OCC读取STEP模型并显示(附完整源码)

qt实现OCC读取STEP模型并显示(附完整源码)

希望我的博客,能帮上你解决学习中工作中所遇到的问题 1480

java sleep源码_Java线程源码解析之yield和sleep

概述由于Thread的yield和sleep有一定的相似性,因此放在一起进行分析。yield会释放CPU资源,让优先级更高(至少是相同)的线程获得执行机会;sleep当传入参数为0时,和yield相同;当传入参数大于0时,也是释放CPU资源,当可以让其它任何优先级的线程获得执行机会;假设当前进程只有main线程,当调用yield之后,main线程会继续运行,因为没有比它优先级更高的线程;而调用sl...

weixin_42108948的博客 608

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

JAVA基础多线程(三)yield、sleep、join、interrupt源码分析

yield、sleep、joinyield()概述例子yield()和wait()的区别sleep()概述sleep()和wait()的区别join() yield() 概述 A hint to the scheduler that the current thread is willing to yield its current use of a processor. The schedul...

Coder Wang 703

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

【java并发】基础(3)--sleep、join、yield

一、sleep()1.1 sleep()简介  sleep() 的作用是让当前线程休眠,即当前线程会从“运行状态”进入到“休眠(阻塞)状态”。在线程重新被唤醒时,它会由“阻塞状态”变成“就绪状态",从而等待cpu的调度执行。   sleep()会指定休眠时间,线程休眠的时间会大于/等于该休眠时间。1.2 sleep()的使用  Thread.sleep(100);1.3 sleep()与wait(

u012512634的专栏 571

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

Thread方法解释(join,sleep,yield,interrupt)

join方法 几种重写的join方法 void join() 等待该线程终止。 void join(long millis) 等待该线程终止的时间最长为 millis 毫秒。 void join(long millis, int nanos) ...

weixin_30571465的博客 244

关于Thread中wait()方法、join()方法、sleep()方法、notify()方法、notifyAll()方法和yield()方法的区别及其联系-含源码和实例

1. join() 2. sleep() 3. yield() 1. join() join()方法作用就是同步,将并行线程变为串行线程 如果在线程A中调用线程B的join()方法则在线程B执行结束之后,线程A再继续执行 join()方法的底层使用的是wait()方法,所以join()方法是同步方法 源码解析: //设置线程等待的时间为毫秒 public final ...

debug 473

Java多线程系列之二 ------------ 常见API,优化的中断线程,线程的几种状态

在学习了第一节之后,我们来看一下Thread常见api的学习。 static methods 以下方法基本上看源码代码,以及翻译一下源码的英文注释。 1 sleep()方法, 使当前正在运行的线程睡眠多少毫秒,这取决于你的系统的时间定时器和调度器,这个线程 不会失去任何监视器的所有权 (线程会阻塞,但是不会释放资源和锁) /** * Causes the...

xzjayx的博客 314

多线程-高级开发人员都必须要深入理解的Thread源码API

深入你的内心,我想想看看真实的你,而不是戴上了面具的你;

知识追寻者(Inheriting the spirit of open source, Spreading technology knowledge;) 538

join sleep yield

join的用法:join是非静态的方法 有线程 threadA 与threadB,同时运行 1、如果在 threadA中调用自身的join方法,则threadA被堵塞,即使threadB结束,threadA也不会解除堵塞。//threadA堵塞threadA,直到threadA结束,发生死锁 2、如果在 threadA中调用自身的join(3000)方法,则threadA被堵塞3000ms,...

weixin_34009794的博客 131

Java并发系列之Java多线程(一)

线程 线程的简介 什么是线程 线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。 为什么要是用多线程 目前的处理器核心越来越多,使用多线程能有更快的响应时间,并能有更好的编程模型。 线程的优先级 现代操作系统基本采用时分的形式调度运行的线程,操作系统分出每一个时间片会根据线程的优先级来分配,优先级越高的最先获取执行资源。 在Java线程中,通过一个整型成员变量priority

羊羊吃草 409

Thread类、Runnable接口详解

前言 Thread类想必都不陌生,第一次学习多线程的时候就一定会接触Thread类。本篇主要从Thread类的定义、使用、注意事项、源码等方面入手,全方位的讲解Thread类。 Thread 我们经常会被问到这样一个问题: Java开启一个新线程有哪几种方法? 答案是两种:继承Thread类、实现Runnable接口。 说只有两种,有人可能就不服了,实现Callable接口为什么不算?线程池为...

Sicimike的博客 6491

Thread类源码解读

本文源码基于jdk1.8 。 一、创建线程有哪几种方式(实现Runnable接口和继承Thread类) Runnable接口 我们看Thread类的定义知道,它实现了Runable接口 public class Thread implements Runnable { ... } 而Runnable接口的定义如下: @FunctionalInterface public interface Runnable { public abstract void run(); } .

码农笔记 823

Java Thread类解析(结合Java源码与openjdk源码)

文章目录Thread类本地方法注册流程及映射关系启动线程:start(),start0() Thread类本地方法注册流程及映射关系 Thread类在类初始化阶段第一件事就是调用registerNatives()注册start0(),interrupt0()等本地方法。 private static native void registerNatives(); static { registerNatives(); } registerNatives()方法在虚拟机

baidu_41650495的博客 704

thread类_万字长文,Thread 类源码解析!

Thread 源码解析金九银十,很多小伙伴都打算跳槽。而多线程是面试必问的,给大家分享下 Thread 源码解析,也算是我自己的笔记整理、思维复盘。学习的同时,顺便留下点什么~1、设置线程名在使用多线程的时候,想要查看线程名是很简单的,调用 Thread.currentThread ().getName () 即可。默认情况下,主线程名是 main,其他线程名是 Thread-x,x 代...

weixin_39631007的博客 251
上一篇: JVM方法常用的宏
下一篇: JVM启动分析
DViewer
博客等级 码龄16年 16粉丝 6原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值