本文转载自微信公众号《飞天小牛》,作者飞天小牛。转载本文请联系飞天小牛公众号。不想看解释的朋友可以直接翻到文末寻找答案。1.用户空间和内核空间关于内核态和用户态,我们学习操作系统,从这篇文章开始。这篇文章已经详细介绍过,这里不再赘述。至于什么是系统空间和用户空间,也很好理解:在操作系统中,内存通常分为两部分:用户空间(Userspace)和内核空间(Kernelspace)。进程/线程运行在用户空间时,就是用户态,运行在内核空间,就是内核态:运行在内核态的程序可以访问用户空间和内核空间,也可以访问任何资源在不受计算机的限制的情况下,为所欲为,如协调CPU资源,分配内存资源,为应用程序提供稳定的运行环境等。应用程序基本上运行在用户态,或者用户态为应用程序提供空间跑步。运行在用户态的程序只能访问用户空间,那为什么要区分用户态和内核态呢?事实上,早期的操作系统并没有区分用户态和内核态,这意味着应用程序可以访问任何内存空间。如果程序不稳定,往往会导致系统崩溃,比如清除操作系统的内存数据。为此,大佬们设计了一套规则:对于那些比较危险的操作,需要切换到内核态运行。例如CPU、内存、设备等资源管理程序应该运行在内核态,否则安全性得不到保障。比如对于文件系统和数据,文件系统的数据和管理必须放在内核态,但是用户数据和管理可以放在用户态。处于用户态的程序不能随意操作内核地址空间,有效地防止了操作系统程序被应用程序侵犯。那么如果一个处于用户态的程序想要访问内核空间怎么办?它需要进行系统调用才能从用户模式切换到内核模式。2.操作系统线程①在用户空间实现线程在早期的操作系统中,所有的线程都是在用户空间实现的。操作系统只能看到线程所属的进程,看不到线程。从我们开发者的角度来理解用户级线程就是:在这个模型下,我们需要自己定义线程的数据结构、创建、销毁、调度、维护。这些线程运行在操作系统的某个进程中。然后操作系统直接调度进程。这种方法的好处一目了然。首先,即使操作系统本身不支持线程,我们也可以通过库函数来支持线程;其次,线程调度只发生在用户态,避免了操作系统从内核态到用户态的转换开销。当然缺点也很明显:由于操作系统看不到线程,不知道线程的存在,而且CPU的时间片切换是基于进程的,所以如果进程中的一个线程执行一个长时间的操作,那么由于用户空间没有时钟中断机制,会导致这个进程中的其他线程因为拿不到CPU资源而等待很长时间;另外,如果一个线程进行系统调用,比如页面错误中断,导致线程阻塞,这时候操作系统也会阻塞整个进程,即使进程中的其他线程还在工作。②在内核空间实现线程所谓内核级线程就是运行在内核空间的线程,由内核直接负责,只能由内核调度。几乎所有现代操作系统,包括Windows、Linux、MacOSX和Solaris,都支持内核线程。每个内核线程都可以看作是内核的一个克隆,这样操作系统就有了同时处理多个事情的能力。支持多线程的内核称为多线程内核(Multi-ThreadsKernel)。从我们开发者的角度理解内核级线程意味着:我们可以直接使用操作系统中内置的线程,线程的创建、销毁、调度和维护都是由操作系统的内核直接实现的.只需要使用系统调用,不需要像用户级线程那样自己设计线程调度。上图显示了1:1线程模型。所谓线程模型就是用户线程和内核线程的关系。当然,线程模型不止是1:1。让我们详细解释以下三种类型的多线程。模型:以下翻译自https://www.cs.uic.edu/~jbell/CourseNotes/OperatingSystems/4_Threads.html1)多对一线程模型:在多对一模型中,多个用户-级别的线程映射到一个内核线程上的线程管理由用户空间的线程库处理,效率很高。但是,如果进行了阻塞系统调用,那么即使其他用户线程能够继续,整个进程也会阻塞。由于单个内核线程只能在单个CPU上运行,因此多对一模型不允许单个进程在多个CPU之间进行拆分。从并发的角度总结一下,虽然多对一的模型允许开发者创建任意多的用户线程,但是由于内核一次只能调度一个线程,所以并没有增加并发。很少有操作系统再使用这种模型,因为它无法利用多个处理核心。2)一对一线程模型:一对一模型克服了多对一模型的问题。一对一模型创建一个单独的内核线程来处理每个用户线程。然而,管理一对一模型的成本更高,涉及更多的开销,并且会减慢系统的速度。该模型的大多数实现都限制了可以创建的线程数。从并发的角度总结一下,而一对一one模型提供更大的并发性,开发人员应注意不要在程序中创建太多线程(有时系统可能会限制创建线程的数量),因为管理一对一模型的开销更大。Windows(自Win95起)和Linux都实现了一对一的线程模型。3)多对多线程模型:多对多模型将任意数量的用户线程复用到相同或更少数量的内核线程,结合了一对一和多对一的最佳特性楷模。阻塞内核系统调用的数量没有限制不会阻塞整个进程进程可以分布在多个处理器上可以根据当前的CPU数量和其他因素将可变数量的内核线程分配给各个进程3.Java线程在进入Java线程话题之前,有必要先解释一下线程库Thread库的概念。在上面的模型介绍中,我们提到了线程是通过线程库来创建和管理的,那么什么是线程库呢?线程库是一组API,为开发者提供线程的创建和管理。当然,线程库不仅可以在用户空间实现,也可以在内核空间实现。前者涉及仅在用户空间实现的API函数,没有内核支持。后者涉及系统调用,即调用库中的API函数会导致对内核的系统调用,需要有线程库支持的内核。下面简单介绍一下三个主要的线程库:1)POSIXPthreads:可以作为用户库或内核库提供,作为POSIX标准的扩展2)Win32线程:Windows操作系统的内核级线程库系统3)Java线程:Java线程API通常由宿主系统的线程库实现,也就是说在Win系统上,Java线程API通常由WinAPI实现,而在UNIX系统上,它是由Pthread实现的。下面详细解释一下Java线程:其实在JDK1.2之前,Java线程都是基于用户级线程实现的,称为“GreenThreads”,也就是说程序员已经为JVM开发了自己的线程。一套线程库或线程管理机制。在JDK1.2及之后的版本中,JVM选择了更稳定、更方便的操作系统原生内核级线程,通过系统调用的方式将线程调度交给了操作系统内核。对于不同的操作系统,其自身的设计思路基本上是完全不同的,因此各自的线程设计也存在着各种差异,因此JVM明确声明:虚拟机中的线程状态不响应任何操作系统中的线程状态。也就是说,在JDK1.2及之后的版本中,Java线程在很大程度上取决于操作系统采用什么样的线程模型。这一点在不同的平台上是无法约定的,JVM规范也没有限定Java线程需要使用哪种线程模型来实现,可能是一对一,也可能是多对多或者多对多一。综上所述,回答下面的问题,今天Java中线程的本质其实就是操作系统中的线程。它的线程库和线程模型在很大程度上取决于操作系统(宿主系统)的具体实现。例如Windows中的Java就是基于Wind32线程库来管理线程的,Windows采用的是一对一的线程模型。参考资料操作系统-线程:https://www.cs.uic.edu/~jbell/CourseNotes/OperatingSystems/4_Threads.htmlJava线程与操作系统线程的关系:https://blog.csdn.net/CringKong/article/细节/79994511?utm_medium
