简介

在传统的操作系统中,进程拥有独立的内存地址空间和一个用于控制的线程。但是,现在的情况更多的情况下要求在同一地址空间下拥有多个线程并发执行。因此线程被引入操作系统。

为什么需要线程?

如果非要说是为什么需要线程,还不如说为什么需要进程中还有其它进程。这些进程中包含的其它迷你进程就是线程。

线程之所以说是迷你进程,是因为线程和进程有很多相似之处,比如线程和进程的状态都有运行,就绪,阻塞状态。这几种状态理解起来非常简单,当进程所需的资源没有到位时会是阻塞状态,当进程所需的资源到位时但CPU没有到位时是就绪状态,当进程既有所需的资源,又有CPU时,就为运行状态。

线程的好处如下:

1.在很多程序中,需要多个线程互相同步或互斥的并行完成工作,而将这些工作分解到不同的线程中去无疑简化了编程模型。

2.因为线程相比进程来说,更加的轻量,所以线程的创建和销毁的代价变得更小。

3.线程提高了性能,虽然线程宏观上是并行的,但微观上却是串行。从CPU角度线程并无法提升性能,但如果某些线程涉及到等待资源(比如IO,等待输入)时,多线程允许进程中的其它线程继续执行而不是整个进程被阻塞,因此提高了CPU的利用率,从这个角度会提升性能。

4.在多CPU或多核的情况下,使用线程不仅仅在宏观上并行,在微观上也是并行的。

经典线程模型

另一个看进程和线程的角度是进程模型基于两类不同的概念:资源的组织和执行。在过去没有线程的操作系统中,资源的组织和执行都是由进程完成的。但区分这两者很多时候需要加以区分,这也是为什么需要引入线程。

进程是用于组织资源的单位,进程将相关的资源组织在一起,这些资源包括:内存地址空间,程序,数据等,将这些以进程的形式组织起来可以使得操作系统管理这些资源更为容易。

而线程,是每一个进程中执行的一个条线。线程虽然共享进程中的大多数资源,但线程也需要自己的一些资源,比如:用于标识下一条执行指令的程序计数器,一些容纳局部变量的寄存器,以及用于表示执行的历史的栈。

总而言之:进程是组织资源的最小单位,而线程是安排CPU执行的最小单位。

其实在一个进程中多个线程并行和在操作系统中多个进程并行非常类似,只是线程共享的是地址空间,而进程共享的是物理内存,打印机,键盘等资源……

每一个进程和线程所独自占有的资源如表1所示。

进程占有的资源 线程占有的资源
地址空间  
全局变量  
打开的文件  
子进程  
信号量  
账户信息
栈  
寄存器  
状态  
程序计数器

表1.进程和线程所独占的资源其中,线程可以共享进程独占的资源。

在多线程的进程中,每个线程轮流使用CPU,因此实际上线程并不是并行的,但从宏观上看,是并行的。

在多线程模型中,每一个进程初始创建时只有一个线程。这个线程可以通过调用系统的库函数去创建其它线程。线程创建的线程并必须要为其指定地址,因为新的线程自动在创建它的地址空间内工作。虽然一个线程可以创建另一个线程,但通常来讲,线程之间是并列的,并不存在层级关系。

当一个进程完成其工作后,可以通过调用系统库函数进行销毁。

操作系统实现线程的几种模式

在操作系统中,线程可以实现在用户模式下,也可以实现在内核模式下,也可以两者结合实现。

线程实现在用户空间下

当线程在用户空间下实现时,操作系统对线程的存在一无所知,操作系统只能看到进程,而不能看到线程。所有的线程都是在用户空间实现。在操作系统看来,每一个进程只有一个线程。过去的操作系统大部分是这种实现方式,这种方式的好处之一就是即使操作系统不支持线程,也可以通过库函数来支持线程。

在这种模式下,每一个进程中都维护着一个线程表来追踪本进程中的线程,这个表中包含表1中每个线程独占的资源,比如栈,寄存器,状态等。

这种模式当一个线程完成了其工作或等待需要被阻塞时,其调用系统过程阻塞自身,然后将CPU交由其它线程。

这种的模式的好处,首先,是在用户空间下进行进程切换的速度要远快于在操作系统内核中实现。其次,在用户空间下实现线程使得程序员可以实现自己的线程调度算法。比如进程可以实现垃圾回收器来回收线程。还有,当线程数量过多时,由于在用户空间维护线程表,不会占用大量的操作系统空间。

有好处就有坏处,这种模式最致命的缺点也是由于操作系统不知道线程的存在,因此当一个进程中的某一个线程进行系统调用时,比如缺页中断而导致线程阻塞,此时操作系统会阻塞整个进程,即使这个进程中其它线程还在工作。还有一个问题是假如进程中一个线程长时间不释放CPU,因为用户空间并没有时钟中断机制,会导致此进程中的其它线程得不到CPU而持续等待。

线程实现在操作系统内核中

在这种模式下,操作系统知道线程的存在。此时线程表存在操作系统内核中;

在这种模式下,所有可能阻塞线程的调用都以系统调用(System Call)的方式实现,相比在用户空间下实现线程造成阻塞的运行时调用(System runtime call)成本会高出很多。当一个线程阻塞时,操作系统可以选择将CPU交给同一进程中的其它线程,或是其它进程中的线程,而在用户空间下实现线程时,调度只能在本进程中执行,直到操作系统剥夺了当前进程的CPU。

因为在内核模式下实现进程的成本更高,一个比较好的做法是另线程回收利用,当一个线程需要被销毁时,仅仅是修改标记位,而不是直接销毁其内容,当一个新的线程需要被创建时,也同样修改被“销毁”的线程其标记位即可。

这种模式下同样还是有一些弊端,比如接收系统信号的单位是进程,而不是线程,那么由进程中的哪一个线程接收系统信号呢?如果使用了表来记录,那么多个线程注册则通过哪一个线程处理系统信号?

混合模式

还有一种实现方式是将上面两种模式进行混合,用户空间中进程管理自己的线程,操作系统内核中有一部分内核级别的线程,如图5所示     在这种模式下,操作系统只能看到内核线程。用户空间线程基于操作系统线程运行。因此,程序员可以决定使用多少用户空间线程以及操作系统线程,这无疑具有更大的灵活性。而用户空间线程的调度和前面所说的在用户空间下执行实现线程是一样的,同样可以自定义实现。

在这种混合实现下,即存在用户线程,也存在轻量级进程。用户线程还是完全建立在用户空间中,因此用户线程的创建、切换、析构等操作依然廉价,并且可以支持大规模的用户线程并发。而操作系统提供支持的轻量级进程则作为用户线程和内核线程之间的桥梁,这样可以使用内核提供的线程调度功能及处理器映射,并且用户线程的系统调用要通过轻量级进程来完成,大大降低了整个进程被完全阻塞的风险。在这种混合模式中,用户线程与轻量级进程的数量比是不定的,即为N:M的关系:

目前来说,作为异步回调以外的另一种解决方案,这种m:n的线程模型可以说大有可为,Golang的协程就是使用了这种模型,在用户态,协程能快速的切换,避免了线程调度的CPU开销问题,协程相当于线程的线程。

协程

类似与线程于进程而言,后面人们又泛化出协程

进程,线程,协程的主要目的是提高效率。而线程和进程是抢占式的程序,在什么时间哪个线程或进程使用cpu是操作系统决定的。操作系统层面我们是无法控制的。而协程是用户可以调度谁先谁后的。yield是协程的一个最底层的实现。

cpu虽然可以分时操作,但是能开启的进程是有限的,尽管线程比较轻量,一个cpu同一时刻只能处理一个线程。如果我要处理的任务是无限,如50000个,假如开了200个线程,这200个线程都阻塞了,那下面的4万多个都动不了。当然,如果一个ie线程中没有IO阻塞,只有计算,cpu就会得到充分利用。但是实际情况中往往IO阻塞非常多,如果阻塞程序就停止,就不能做其他事情了,虽然操作系统会调度其他进程或线程工作,但是当前的进程还是会有分配给他的时间片,而他实际是阻塞时还占用着cpu,这是对cpu的浪费。
而进程,线程都会占用系统资源,在他们之间切换也会浪费一些时间,所以在高并发越来越重要的今天,使用线程或进程就不能满足我们了。
所以有了协程:也叫纤程,对于cpu来说,线程是他执行的最小单位,也就是说他只能看到线程,协程是看不到的。
一条线程 在多个任务之间来回切换,切换这个动作是浪费时间的。对于cpu,操作系统来说,协程是不存在的,他们只管执行线程。他们不管你执行哪个任务,只管执行线程的指令。

纤程(Fiber)

和协程是一个东西,只不过在windows下叫纤程。

有一些参考资料:https://blog.csdn.net/BIackMamba/article/details/92983852?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase

2、https://www.cnblogs.com/CareySon/archive/2012/05/04/ProcessAndThread.html

3、https://blog.csdn.net/CringKong/article/details/79994511

4、https://www.jianshu.com/p/dd4a480a1410

操作系统中的进程与线程和java中的线程相关推荐

  1. Java中的Volatile如何工作? Java中的volatile关键字示例

    如何在Java中使用Volatile关键字 在Java采访中,什么是volatile变量以及何时在Java中使用volatile变量是Java 采访中一个著名的多线程采访问题 . 尽管许多程序员都知道 ...

  2. opengl中的Floatbuffer和IntBuffer与java中数据的存储方式不同的解决方法,编辑一个自己的BufferUtil工具类

    opengl中的Floatbuffer和IntBuffer与java中数据的存储方式不同的解决方法,编辑一个自己的BufferUtil工具类 参考文章: (1)opengl中的Floatbuffer和 ...

  3. java中的io复用_从 Java 中的零拷贝到五种IO模型

    在之前的文章中,我们聊过了 Java 中的零拷贝,零拷贝就是指数据不会在内核空间和用户空间之间相互拷贝.这样就减少了内核态与用户态的切换,自然就很高效. 拷贝文件只是 IO 操作中一个特殊的情况,大多 ...

  4. javac 与java_javac中使用-d,以及javac / java中使用-cp介绍

    javac中使用-d,以及javac / java中使用-cp介绍 发布时间:2019-01-15 20:39, 浏览次数:555 , 标签: javac java cp * 一般生成把java编译成 ...

  5. java线程和内核线程的,Java中内核线程理论及实例详解

    1.概念 内核线程是直接由操作系统内核控制的,内核通过调度器来完成内核线程的调度并负责将其映射到处理器上执行.内核态下的线程执行速度理论上是最高的,但是用户不会直接操作内核线程,而是通过内核线程的接口 ...

  6. java中线程总结,JAVA中线程的相关小结

    ·什么是线程 线程:进程中负责程序执行的执行单元.一个进程中至少有一个线程. 多线程:一个进程中包含有多个线程,但CPU在同一时间只允许一个线程的进行.所以有多个线程的运行是根据CPU切换完成,如何切 ...

  7. 用java线程绘制图案_关于多线程:使用线程在java中绘制面板

    我正在编写一个包含许多不同视图的程序. 其中一个是相当图形密集型(它显示一个互连的图形). 其他人只是展示小而复杂的图表. 我发现主视图的绘制时间很长(甚至只绘制当前可见的区域),并且在绘制时,界面的 ...

  8. java 线程aba,JAVA中CAS-ABA的问题解决方案AtomicStampedReference

    了解CAS(Compare-And-Swap) CAS即对比交换,它在保证数据原子性的前提下尽可能的减少了锁的使用,很多编程语言或者系统实现上都大量的使用了CAS. JAVA中CAS的实现 JAVA中 ...

  9. java中的单例_细说Java中的几种单例模式

    在Java中,单例模式分为很多种,本人所了解的单例模式有以下几种,如有不全还请大家留言指点: 饿汉式 懒汉式/Double check(双重检索) 静态内部类 枚举单例 一.饿汉式 image 饿汉式 ...

最新文章

  1. 【复盘】升级打怪第一关,冲啊!
  2. kali Linux metasploit进入msfgui
  3. 无服务器计算的黑暗面:程序移植没那么容易
  4. Spring Cloud Sleuth进阶实战
  5. CSDN公众号新功能上线,居然还能搜出小姐姐???(文末有福利)
  6. NOIP2013D1T3货车运输(最大生成树+倍增lca)
  7. wpp助手怎么连接服务器,aewpp.com
  8. ASP.NET之通过JS向服务端(后台)发出请求(__doPostBack is undefined)
  9. 使用Visual Studio 2012 开发 Html5 应用
  10. 教你一键式下载iOS旧版APP(2021年有效)
  11. 科来网络分析系统(实用)
  12. 移动端日历插件_“滴答清单”移动端产品分析报告
  13. 贪心算法 看这一篇就够了
  14. 如何在matlab坐标轴上输入希腊字符和开根号符号
  15. OpenCV3.4.x移植到嵌入式ARM板
  16. Wed Aug 03 19:48:03 +0800 2022这种字符串,怎么转成时间格式年月日
  17. B站傅希鸣-ElasticSearch学习笔记(ES 入门)
  18. 软件项目管理之历时估算(工程评估评审技术【Program Evaluation And Review Technique,PERT】)
  19. mysql模糊搜索 like_Mysql必知必会(3):模糊查询(LIKE)
  20. C# 实现 简体<--->繁体 的互相转换

热门文章

  1. OO Design之SOLID原则
  2. Android 监听home键(android:launchMode=singleTask 与 onNewIntent(Intent intent) 的用法
  3. 简单是一种美:提高项目成功率的一些方法
  4. 查询方式中断方式_【每日“一”题】中断方式
  5. python整数和浮点数相乘_python中整数除法和浮点数到整数转换之间的区别是什么原因?...
  6. php 删除 r n,PHP去除换行符'/r/n'回车换行与PHP_EOL变量的使用
  7. php课程实验总结报告_PHP课程总结20161125
  8. php下载文件代码详解,php将远超文件下载到本地的示例代码详解
  9. java 继承 私有变量_java – 继承和私有变量
  10. java md5 密钥_java加密算法--MD5加密和哈希散列带秘钥加密算法源码