Java 线程和操做系统的线程有啥区别?

不想看解释的小伙伴可直接翻到文末寻找答案。html

1. 用户空间和内核空间git

关于内核态和用户态咱们在 了解操做系统的那些事儿,从这篇文章开始 这篇文章中已经详细介绍过,这里再也不过多赘述。程序员

至于什么是系统空间和用户空间也很是好理解:在操做系统中,内存一般会被分红用户空间(User space)与内核空间(Kernel space)这两个部分。当进程/线程运行在用户空间时就处于用户态,运行在内核空间时就处于内核态:web

  • 运行在内核态的程序能够访问用户空间和内核空间,或者说它能够访问计算机的任何资源,不受限制,随心所欲,例如协调 CPU 资源,分配内存资源,提供稳定的环境供应用程序运行等
  • 而应用程序基本都是运行在用户态的,或者说用户态就是提供应用程序运行的空间。运行在用户态的程序只能访问用户空间

那为何要区分用户态和内核态呢安全

其实早期操做系统是不区分用户态和内核态的,也就是说应用程序能够访问任意内存空间,若是程序不稳定经常会让系统崩溃,好比清除了操做系统的内存数据。为此大佬们设计出了一套规则:对于那些比较危险的操做须要切到内核态才能运行,好比 CPU、内存、设备等资源管理器程序就应该在内核态运行,不然安全性没有保证。微信

举个例子,对于文件系统和数据来讲,文件系统数据和管理就必须放在内核态,可是用户的数据和管理能够放在用户态。数据结构

用户态的程序不能随意操做内核地址空间,这样有效地防止了操做系统程序受到应用程序的侵害多线程

那若是处于用户态的程序想要访问内核空间的话怎么办呢?就须要进行系统调用从用户态切换到内核态。并发

2. 操做系统线程

① 在用户空间中实现线程

早期的操做系统中,全部的线程都是在用户空间下实现的,操做系统只能看到线程所属的进程,而不能看到线程。app

从咱们开发者的角度来理解用户级线程就是说:在这种模型下,咱们须要本身定义线程的数据结构、建立、销毁、调度和维护等,这些线程运行在操做系统的某个进程内,而后操做系统直接对进程进行调度。

这种方式的好处一目了然,首先第一点,就是即便操做系统原生不支持线程,咱们也能够经过库函数来支持线程;第二点,线程的调度只发生在用户态,避免了操做系统从内核态到用户态的转换开销。

固然缺点也很明显:因为操做系统看不见线程,不知道线程的存在,而 CPU 的时间片切换是以进程为维度的,因此若是进程中某个线程进行了耗时比较长的操做,那么因为用户空间中没有时钟中断机制,就会致使此进程中的其它线程由于得不到 CPU 资源而长时间的持续等待;另外,若是某个线程进行系统调用时好比缺页中断而致使了线程阻塞,此时操做系统也会阻塞住整个进程,即便这个进程中其它线程还在工做。

② 在内核空间中实现线程

所谓内核级线程就是运行在内核空间的线程, 直接由内核负责,只能由内核来完成线程的调度。

几乎全部的现代操做系统,包括 Windows、Linux、Mac OS X 和 Solaris 等,都支持内核线程。

每一个内核线程能够视为内核的一个分身,这样操做系统就有能力同时处理多件事情,支持多线程的内核就叫作多线程内核(Multi-Threads Kernel)。

从咱们开发者的角度来理解内核级线程就是说:咱们能够直接使用操做系统中已经内置好的线程,线程的建立、销毁、调度和维护等,都是直接由操做系统的内核来实现,咱们只须要使用系统调用就行了,不须要像用户级线程那样本身设计线程调度等。

上图画的是 1:1 的线程模型,所谓线程模型,也就是用户线程和内核线程之间的关联方式,线程模型固然不止 1:1 这一种,下面咱们来详细解释如下这三种多线程模型:

下文翻译自 https://www.cs.uic.edu/~jbell/CourseNotes/OperatingSystems/4_Threads.html

1)多对一线程模型

  • 在多对一模型中,多个用户级线程映射到某一个内核线程上
  • 线程管理由用户空间中的线程库处理,这很是有效
  • 可是,若是进行了阻塞系统调用,那么即便其余用户线程可以继续,整个进程也会阻塞
  • 因为单个内核线程只能在单个 CPU 上运行,所以多对一模型不容许在多个 CPU 之间拆分单个进程

从并发性角度来总结下,虽然多对一模型容许开发人员建立任意多的用户线程,可是因为内核只能一次调度一个线程,因此并未增长并发性。如今已经几乎没有操做系统来使用这个模型了,由于它没法利用多个处理核。

2)一对一线程模型

  • 一对一模型克服了多对一模型的问题
  • 一对一模型建立一个单独的内核线程来处理每一个用户线程
  • 可是,管理一对一模型的开销更大,涉及更多开销和减慢系统速度
  • 此模型的大多数实现都限制了能够建立的线程数

从并发性角度来总结下,虽然一对一模型提供了更大的并发性,可是开发人员应注意不要在应用程序内建立太多线程(有时系统可能会限制建立线程的数量),由于管理一对一模型的开销更大。Windows (从 Win95 开始) 和 Linux 都实现了线程的一对一模型

3)多对多线程模型

  • 多对多模型将任意数量的用户线程复用到相同或更少数量的内核线程上,结合了一对一和多对一模型的最佳特性
  • 用户对建立的线程数没有限制
  • 阻止内核系统调用不会阻止整个进程
  • 进程能够分布在多个处理器上
  • 能够为各个进程分配可变数量的内核线程,具体取决于存在的 CPU 数量和其余因素

3. Java 线程

在进入 Java 线程主题以前,有必要讲解一下线程库 Thread library 的概念。

在上面的模型介绍中,咱们提到了经过线程库来建立、管理线程,那么什么是线程库呢?

线程库就是为开发人员提供建立和管理线程的一套 API

固然,线程库不只能够在用户空间中实现,还能够在内核空间中实现。前者涉及仅在用户空间内实现的 API 函数,没有内核支持。后者涉及系统调用,也就是说调用库中的一个 API 函数将会致使对内核的系统调用,而且须要具备线程库支持的内核。

下面简单介绍下三个主要的线程库:

1)POSIX Pthreads:能够做为用户或内核库提供,做为 POSIX 标准的扩展

2)Win32 线程:用于 Window 操做系统的内核级线程库

3)Java 线程:Java 线程 API 一般采用宿主系统的线程库来实现,也就是说在 Win 系统上,Java 线程 API 一般采用 Win API 来实现,在 UNIX 类系统上,采用 Pthread 来实现。

下面咱们来详细讲解 Java 线程:

事实上,在 JDK 1.2 以前,Java 线程是基于称为 "绿色线程"(Green Threads)的用户级线程实现的,也就是说程序员大佬们为 JVM 开发了本身的一套线程库或者说线程管理机制。

在 JDK 1.2 及之后,JVM 选择了更加稳定且方便使用的操做系统原生的内核级线程,经过系统调用,将线程的调度交给了操做系统内核。而对于不一样的操做系统来讲,它们自己的设计思路基本上是彻底不同的,所以它们各自对于线程的设计也存在种种差别,因此 JVM 中明确声明了:虚拟机中的线程状态,不反应任何操做系统中的线程状态

也就是说,在 JDK 1.2 及以后的版本中,Java 的线程很大程度上依赖于操做系统采用什么样的线程模型,这点在不一样的平台上没有办法达成一致,JVM 规范中也并未限定 Java 线程须要使用哪一种线程模型来实现,多是一对一,也多是多对多或多对一。

总结来讲,回答下文题,现今 Java 中线程的本质,其实就是操做系统中的线程,其线程库和线程模型很大程度上依赖于操做系统(宿主系统)的具体实现,好比在 Windows 中 Java 就是基于 Wind32 线程库来管理线程,且 Windows 采用的是一对一的线程模型

References

  • Operating Systems - Threads:https://www.cs.uic.edu/~jbell/CourseNotes/OperatingSystems/4_Threads.html
  • Java 线程和操做系统线程的关系:https://blog.csdn.net/CringKong/article/details/79994511?utm_medium



  • 博主小硕在读,深耕 Java,目前在维护一个教程类仓库 CS-Wiki 「Gitee 官方推荐项目,现已 1.5k+ star,仓库地址:https://gitee.com/veal98/CS-Wiki」,公众号上的文章也会在此同步更新,欢迎各位前来交流学习。
  • 准备春招秋招的小伙伴能够参考个人这个论坛项目 Echo 「Gitee 官方推荐项目,现已 600+ star,仓库地址:https://gitee.com/veal98/Echo」。配套教程正在同步更新中,公众号后台回复 "Echo" 便可免费获取。
  • 另外,虽然如今本号仍然很小,粉丝也没多少,不过我仍是建了一个交流群『 小牛肉和它的小伙伴们 』,感兴趣的各位能够下方扫码加我微信回复 "进群",我拉你进群:

本文分享自微信公众号 - 飞天小牛肉(CS-Wiki)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。

相关文章
相关标签/搜索