• 深入理解java虚拟机笔记java内存模式与线程3


    一、java与线程

    1.1 线程的实现

    我们知道, 线程是比进程更轻量级的调度执行单位, 线程的引入, 可以把一个进程的资源分配和执行调度分开, 各个线程既可以共享进程资源(内存地址、 文件I/O等) , 又可以独立调度。 目前线程是Java里面进行处理器资源调度的最基本单位。

    主流的操作系统都提供了线程实现, Java语言则提供了在不同硬件和操作系统平台下对线程操作的统一处理, 每个已经调用过start()方法且还未结束的java.lang.Thread类的实例就代表着一个线程。 我们注意到Thread类与大部分的Java类库API有着显著差别, 它的所有关键方法都被声明为Native。

    在Java类库API中, 一个Native方法往往就意味着这个方法没有使用或无法使用平台无关的手段来实现(当然也可能是为了执行效率而使用Native方法, 不过通常最高效率的手段也就是平台相关的手段) 。 正因为这个原因,本节的标题被定为“线程的实现”而不是“Java线程的实现”, 在稍后介绍的实现方式中, 我们也先把Java的技术背景放下, 以一个通用的应用程序的角度来看看线程是如何实现的。

    实现线程主要有三种方式: 使用内核线程实现(1: 1实现) , 使用用户线程实现(1: N实现) ,使用用户线程加轻量级进程混合实现(N: M实现) 。

    1.内核线程实现

    使用内核线程实现的方式也被称为1: 1实现。 内核线程(KLT)就是直接由操作系统内核支持的线程, 这种线程由内核来完成线程切换, 内核通过操纵调度器(Scheduler) 对线程进行调度, 并负责将线程的任务映射到各个处理器上。 每个内核线程可以视为内核的一个分身, 这样操作系统就有能力同时处理多件事情, 支持多线程的内核就称为多线程内核 。

    程序一般不会直接使用内核线程, 而是使用内核线程的一种高级接口——轻量级进程(Light Weight Process, LWP) , 轻量级进程就是我们通常意义上所讲的线程, 由于每个轻量级进程都由一个内核线程支持, 因此只有先支持内核线程, 才能有轻量级进程。 这种轻量级进程与内核线程之间1: 1的关系称为一对一的线程模型, 如图12-3所示

    由于内核线程的支持, 每个轻量级进程都成为一个独立的调度单元, 即使其中某一个轻量级进程在系统调用中被阻塞了, 也不会影响整个进程继续工作。 轻量级进程也具有它的局限性: 首先, 由于是基于内核线程实现的, 所以各种线程操作, 如创建、 析构及同步, 都需要进行系统调用。 而系统调用的代价相对较高, 需要在用户态(User Mode) 和内核态(Kernel Mode) 中来回切换。

    其次, 每个轻量级进程都需要有一个内核线程的支持, 因此轻量级进程要消耗一定的内核资源(如内核线程的栈 空间) , 因此一个系统支持轻量级进程的数量是有限的。

    2.用户线程实现

    使用用户线程实现的方式被称为1: N实现。 广义上来讲, 一个线程只要不是内核线程, 都可以认为是用户线程(User Thread, UT) 的一种, 因此从这个定义上看, 轻量级进程也属于用户线程, 但轻量级进程的实现始终是建立在内核之上的, 许多操作都要进行系统调用, 因此效率会受到限制, 并不具备通常意义上的用户线程的优点。

    而狭义上的用户线程指的是完全建立在用户空间的线程库上, 系统内核不能感知到用户线程的存在及如何实现的。

    用户线程的建立、 同步、 销毁和调度完全在用户态中完成, 不需要内核的帮助。 如果程序实现得当, 这种线程不需要切换到内核态, 因此操作可以是非常快速且低消耗的, 也能够支持规模更大的线程数量, 部分高性能数据库中的多线程就是由用户线程实现的。 这种进程与用户线程之间1: N的关系称为一对多的线程模型, 如图12-4所示。 用户线程的优势在于不需要系统内核支援, 劣势也在于没有系统内核的支援, 所有的线程操作都需要由用户程序自己去处理。 线程的创建、 销毁、 切换和调度都是用户必须考虑的问题, 而且由于操作系统只把处理器资源分配到进程, 那诸如“阻塞如何处理”“多处理器系统中如何将线程映射到其他处理器上”这类问题解决起来将会异常困难, 甚至有些是不可能实现的。

    因为使用用户线程实现的程序通常都比较复杂, 除了有明确的需求外 , 一般的应用程序都不倾向使用用户线程。 Java、 Ruby等语言都曾经使用过用户线程, 最终又都放弃了使用它。 但是近年来许多新的、 以高并发为卖点的编程语言又普遍支持了用户线程, 譬如Golang、 Erlang等, 使得用户线程的使用率有所回升。

    3.混合实现

    线程除了依赖内核线程实现和完全由用户程序自己实现之外, 还有一种将内核线程与用户线程一起使用的实现方式, 被称为N: M实现。 在这种混合实现下, 既存在用户线程, 也存在轻量级进程。

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

    许多UNIX系列的操作系统, 如Solaris、 HP-UX等都提供了M: N的线程模型实现。 在这些操作系统上的应用也相对更容易应用M: N的线程模型。

    4.Java线程的实现

    Java线程如何实现并不受Java虚拟机规范的约束, 这是一个与具体虚拟机相关的话题。从JDK 1.3起, “主流”平台上的“主流”商用Java虚拟机的线程模型普遍都被替换为基于操作系统原生线程模型来实现, 即采用1: 1的线程模型。

    以HotSpot为例, 它的每一个Java线程都是直接映射到一个操作系统原生线程来实现的, 而且中间没有额外的间接结构, 所以HotSpot自己是不会去干涉线程调度的 , 全权交给底下的操作系统去处理, 所以何时冻结或唤醒线程、 该给线程分配多少处理器执行时间、 该把线程安排给哪个处理器核心去执行等, 都是由操作系统完成的,也都是由操作系统全权决定的。

    操作系统支持怎样的线程模型, 在很大程度上会影响上面的Java虚拟机的线程是怎样映射的, 这一点在不同的平台上很难达成一致, 因此《Java虚拟机规范》 中才不去限定Java线程需要使用哪种线程模型来实现。

    线程模型只对线程的并发规模和操作成本产生影响, 对Java程序的编码和运行过程来说, 这些差异都是完全透明的。 此处所讲的“复杂”与“程序自己完成线程操作”, 并不限于程序直接编写了复杂的实现用户线程的代码, 使用用户线程的程序时, 很多都依赖特定的线程库来完成基本的线程操作, 这些复杂性都封装在线程库之中

  • 相关阅读:
    [虚拟化/云][全栈demo] 为qemu增加一个PCI的watchdog外设(二)
    PHP中PDO的配置与说明
    PHP输出表格的方法
    打印网格版本9*9乘法表
    js中日历的代码
    js中初学函数的使用
    c#中的23种设计模式
    用多态来实现U盘,Mp3,移动硬盘和电脑的对接,读取写入数据。
    用面向对象多态的思想分别去求圆形和长方形的面积和周长
    面向对象之多态(抽象类)
  • 原文地址:https://www.cnblogs.com/wangbin2188/p/16039075.html
Copyright © 2020-2023  润新知