java并发系列——从计算机体系结构看java线程模型

x33g5p2x  于2022-02-12 转载在 Java  
字(2.1k)|赞(0)|评价(0)|浏览(222)

计算机结构

正如我们熟知,现代机器可以分为硬件跟软件两大块。硬件是基础,软件提供了实现不同功能的手段。软件可以分为操作系统和应用程序,操作系统专注于对硬件的交互管理并提供一个运行环境给应用程序使用,而应用程序则是能实现若干功能的并且运行在操作系统环境中的软件。

线程模型

当我们谈起Java多线程时肯定就会涉及到Java多线程模型,而且也将涉及Java线程与底层操作系统之间的关系。线程按照操作系统和应用程序这两个层次可以分为内核线程(Kernel Thread)和用户线程(User Thread),这也很好理解,毕竟操作系统分为内核态和用户态。

所谓内核线程就是直接由操作系统内核支持和管理的线程,线程的建立、启动、同步、销毁、切换等操作都由内核完成,基本所有的现代操作系统都支持内核线程。

用户线程指完全建立在用户空间的线程库上,由内核支持但无需内核管理,内核也无法感知用户线程的存在。线程的建立、启动、同步、销毁、切换完全是在用户态下完成,无需切换到内核态。

可以将用户线程看成是更高层面的线程,而内核线程则是最底层的支持。这样一来它们之间必然存在着一定的映射关系,实际上一般存在三种常见的关系,下面逐个介绍。

一对一模型

一对一模型可以说是最简单的映射模型。如图所示,KT为内核线程,UT为用户线程。每个用户线程都对应一个内核线程,由于每个用户线程都有各自的内核线程,所以它们互不影响。即使一个线程阻塞,另一个线程也可以继续执行。互不影响是该模型的优点,但是同时也存在一个严重的缺陷。由于是一对一的关系,所以有多少个用户线程就必须要有多少个内核线程,这将导致内核线程的总开销很大。所以,操作系统一般都会有内核线程的数量限制,这样也导致用户线程数量受限。

多对一模型

第二种是多对一模型,如图所示,可以清晰看到多个用户线程映射到一个内核线程上。从另一个角度看,我们可以将其看成由一条内核线程实现若干个用户线程的并发功能。而且线程的管理在用户空间中进行,一般不需要切换到内核态,这样的效率比较高。

对比一对一模型,多对一模型支持的线程数量更大。但该模型存在一个致命的弱点,那就是如果一个线程阻塞了将导致所有映射到该内核线程的用户线程阻塞。此外,任意时刻只能有一个用户线程访问内核,而且对线程的所有操作都将由用户应用自己处理。所以一般除了在不支持多线程的操作系统被迫使用该模型外,该模型基本不会被使用。

多对多模型

一对多模型受内核线程数限制,而多对一模型虽然解决了内核线程数限制的问题,但也带来了线程阻塞的风险,同时也破坏了并发性。所以下面有一种折衷的方案。

多对多模型的提出是为了解决前面两种模型带来的问题,如图所示,多个用户线程与多个内核线程映射成多路复用。由于多对一是多对多的子集,所以多对多具备多对一的优点,那就是线程数不受限制。除此之外,多个内核线程可处理多个用户线程,当某个线程阻塞时,可以调度到另一个内核线程处理,这也增强了线程间的并发性。

 三种模型总结

三种模型各自有各自的特点,不同的现代操作系统可能会使用不同的线程模型,例如Linux和Windows可能使用了一对一模型,而Solaris和Unix某些版本可能使用了多对多模型。线程的创建和管理主要由线程库提供用户级别和内核级别两种API进行操作。用户级别由于不涉及到内核操作,所有代码和数据结构均存放在用户空间。与此相反,内核级别则由内核支持,将直接调用内核系统操作,代码和数据结构存在于内核空间中。

轻量级进程

在实际编程中我们一般不直接使用内核线程,用户线程与内核线程之间需要有一种中间数据结构,它由内核支持且是内核线程的高级抽象,这种高级接口被称为轻量级进程(Light Weight Process),下面简称LWP。

从某种层面上看,LWP最多算是广义的用户线程,并非狭义定义的用户进程。LWP线程库以内核为基础,很多操作要进行内核调用,效率不高。如果要进行快速低消耗的操作则需要一个纯粹的用户线程,于是可以看到一个进程P里面一般包含若干个用户线程,而且用户线程以某种关系对应轻量级进程。一个内核线程阻塞将导致LWP也阻塞,与LWP相连的用户线程也将阻塞。

java与操作系统

最后要谈一下Java线程与操作系统的关系,由于Java语言通过JVM来封装底层操作系统的差异,所以Java线程也必然会将不同操作系统的线程进行封装,并提供一个统一的并发定义。

在JDK发展史上,Java语言开发者曾经通过一类叫“绿色线程(Green Threads)”的用户线程来实现Java线程。但从JDK 1.2开始,Java线程改用操作系统原生线程模型来实现,也就是说Java线程的实现是通过不同操作系统提供的线程库来分别实现的。JVM会根据不同操作系统的线程模型对Java线程进行映射,假如Java运行在Windows上,它通常直接使用Win32 API实现多线程。加入Java运行在Linux上则直接使用Pthread线程库实现多线程,这样一来就隐藏了线程底层的实现细节,提供给开发者就是一个统一的抽象线程语义。

相关文章

微信公众号

最新文章

更多