专栏首页IT界的泥石流用户级多线程的切换原理

用户级多线程的切换原理

前言

上文(从多进程到多线程)中,我们讨论了何为多线程,而线程又分用户级线程和内核级线程,这节我们先来讨论一下何为用户级线程以及用户级线程的底层原理

用户级线程和内核级线程

说到用户级多线程,我们有必要先来简单对比一下用户级线程和内核级线程之间的区别。

内核级线程,顾名思义,它的调度是依赖于操作系统的,即操作系统控制着内核级线程的切换,比如有A和B两个内核级线程,我们用户是不知道先执行哪个线程的代码和不知道什么时候切换到另一个线程执行代码的,这件事只有操作系统知道,我们无法干预。

用户级线程,顾名思义,它的调度是依赖于用户的想法的,比如有C和D两个用户级线程,我们用户可以先让A执行一段代码后,然后手动控制让其跳到B去执行一段代码,我们是清楚知道线程间的切换的。

简单一句话来说就是:内核级线程是由操作系统进行调度的,用户级线程是由用户来控制调度的。

用户级线程

我们举例子,来进一步说明用户级线程切换的底层原理,还是记住那句话:用户级线程的切换是由我们用户来主动控制的。

现在我们假设有线程1和线程2两个线程(图中红色的数字为内存的地址)

可以看出,线程1中有A()和B()两个函数,执行流程为A()函数调用B()函数,B()函数执行完毕后返回到地址为104的语句继续往下执行;线程2中有C()和D()两个函数,执行流程为C()函数调用D()函数,D()函数执行完毕后返回到地址为304的语句继续往下执行。那么图中还有一个Yield()函数到底是什么东西呢,简单来说它就是我们用户主动来控制线程切换的一个函数,在线程1中调用Yield()函数,此时会切换到线程2,在线程2中调用了Yield()函数,此时又会回到线程1继续执行。因此,执行流程为下图所示。

现在我们更加深入地去剖析整个切换过程到底发生了什么有趣的事。

线程1运行:B() 为函数调用,此时将函数调用的下一条指令地址入栈,即104入栈(因为要记录函数调用结束后返回到哪里继续执行)。

随即进入B()内执行 ——> 线程切换(用户级线程)函数Yield()也为函数调用(特殊函数调用),即将要执行Yield(),则204入栈,随即执行Yield() ——> 跳至内存中地址为300的指令执行,此时即进行了线程切换。

类似,执行C() ——> 调用D(),304入栈,即将执行Yield(),404入栈。

随即执行线程2的Yield() ——> 切换至线程1执行204 ,之后B() 结束,弹栈,为404 ,即执行完后跳去地址为404处执行。而实际上我们开始说过,执行完B()后要按轨迹5返回线程1的地址为104处执行,而此时调用执行完进入线程2的404,出错。

某线程中的普通函数调用(Yield是特殊函数调用)只能在本线程来回折腾,所以上述情况的普通函数调用会返回到另外的线程执行,这是错误的。

出错原因是因为两个线程共用了一个栈,导致线程之间切换和内部运行出现错乱。

因此,可以用两个栈解决这个问题,即分别为每一个线程分配一个独立的栈!

还是上面的那个例子

此时的栈不在是上面的那仅有的一个栈,而是线程1有一个栈,线程2也有一个栈,其中它们各自的栈地址存放于各自线程的TCB中,还有一个全局的栈指示变量,它的作用是指示当前用的是哪一个线程的栈。

那么,线程1运行:B() 为函数调用,此时将函数调用的下一条指令地址入栈,即104入线程1中的栈,B()函数中执行Yield()函数,204入线程1中的栈,然后切换到线程2执行。

线程2执行,同理,304入线程2的栈,404入线程2的栈。

此时,两个栈的状态如下图

线程2执行Yield()函数后,全局栈指示变量指向线程1的栈,此时执行出栈操作,出的是线程1的栈,弹出204,转到204执行语句,接着B()函数调用完后,return,继续在线程1的栈中(因为全局栈指示变量依然指向线程1的栈)执行出栈操作,弹出104,转回A()函数中的104语句,此时,就完美地解决了使用一个栈会导致乱跳转的问题!

这一节,我们讲述了内核级线程和用户级线程的基本概念与区别、用户级线程的切换底层原理

本文分享自微信公众号 - IT界的泥石流(gh_888495c5eb2c)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-07-08

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • java面试必问之ThreadLocal

    JDK 1.2的版本开始提供java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很...

    xujjj
  • 设计模式(五):最少知识原则

    最少知识原则的主要思想是:类之间应松耦合。类之间耦合度越低,越有利于复用。当松耦合的类被修改时,不会影响到其他类的使用。

    xujjj
  • 如何用20行代码实现植物大战僵尸秒杀僵尸的功能

    外挂的本质其实就是找到进程中的某个变量或者某行代码的内存地址,然后进行修改,完成其想要完成的功能。这是最基本的要求,当然如果想要深入去学习制作外挂的话,还有很多...

    xujjj
  • 深入理解 Java 多线程核心知识:跳槽面试必备

    小柒2012
  • Java中的线程池用过吧?来说说你是怎么理解线程池吧?

    Java中的线程池用过吧?来说说你是怎么使用线程池的?这句话在面试过程中遇到过好几次了。我甚至这次标题都想写成【Java八股文之线程池】,但是有点太俗套了。虽然...

    纪莫
  • 你管这叫"线程安全"?

    1.什么叫线程安全?2.线程安全与变量的关系?•变量又与堆/栈/静态存储区有密切关系

    小码甲
  • 以为精通Java 线程池,看到这些误区,还是年轻了...

    由于线程的创建和销毁对操作系统来说都是比较重量级的操作,所以线程的池化在各种语言内都有实践,当然在 Java 语言中线程池是也非常重要的一部分,有 Doug L...

    程序员小强
  • 这一次彻底搞懂Java的Lock接口到底有什么用!

    这俩问题,管程都能一把梭。JUC是通过Lock、Condition接口实现的管程:

    JavaEdge
  • Java 线程池配置的常见误区

    由于线程的创建和销毁对操作系统来说都是比较重量级的操作,所以线程的池化在各种语言内都有实践,当然在 Java 语言中线程池是也非常重要的一部分,有 Doug L...

    Java小咖秀
  • 多线程面试题(2021最新版)

    现在不管是大公司还是小公司,去面试都会问到多线程与并发编程的知识,大家面试的时候这方面的知识一定要提前做好储备。

    Java程序猿

扫码关注云+社区

领取腾讯云代金券