Java 并发编程解析 | 如何正确理解Java领域中的多线程模型,主要用来解决什么问题?

作为一名Java Developer,在面对Java并发编程的时候,有过哪些的疑惑与不解 ?对于Java领域中的线程机制与多线程,你都做了哪些功课?是否和我一样,在看完《Java编程思想》和《Java并发编程实战》之后,依旧一头雾水,不知其迹?那么,希望你看完此篇文章之后,对你有所帮助...
摘要由CSDN通过智能技术生成

我国宋代禅宗大师青原行思在《三重境界》中有这样一句话:“ 参禅之初,看山是山,看水是水;禅有悟时,看山不是山,看水不是水;禅中彻悟,看山仍然山,看水仍然是水。”

作为一名Java Developer,在面对Java并发编程的时候,有过哪些的疑惑与不解 ?对于Java领域中的线程机制与多线程,你都做了哪些功课?是否和我一样,在看完《Java编程思想》和《Java并发编程实战》之后,依旧一头雾水,不知其迹?那么,希望你看完此篇文章之后,对你有所帮助。

从一定程度上说,Java并发编程之路,实则是一条“看山是山,看山不是山,看山还是山”的修行之路。大多数情况下,当我们觉得有迹可循到有迹可寻时,何尝不是陷入了另外一个“怪圈”之中?

从搭载Linux系统上的服务器程序来说,使用Java编写的是”单进程-多线程"程序,而用C++语言编写的,可能是“单进程-多线程”程序,“多进程-单线程”程序或者是“多进程-多线程”程序。其中,“多进程-多线程”程序是”单进程-多线程"程序和“多进程-单线程”程序的组合体。

相对于操作系统内核来说,Java程序属于应用程序,只能在这一个进程里面,一般我们都是直接利用JDK提供的API开发多个线程实现并发。

而C++直接运行在Linux系统上,可以直接利用Linux系统提供的强大的进程间通信(Inter-Process Communication,IPC),很容易创建多个进程实现并发程序,并实现进程间通信。

但是,多线程的开发难度远远高于单线程的开发,主要是需要处理线程间的通信,需要对线程并发做控制,需要做好线程间的协调工作。

对于固定负载情况下,在描述和研究计算并发系统处理能力,以及描述并行处理效果的加速比,一直有一个比较著名的计算公式:

就是我们熟知的阿姆达尔定律(Amdahl"s Law),在这个公式中,

[1]. P:指的是程序中可并行部分的程序在单核上执行的时间占比。一般用作表示可改进性能的部件原先运行占用的时间与系统整体运行需要的时间的比值,取值范围是0 ≤ P ≤ 1。

[2]. S:指的是处理器的个数(总核心数)。一般用作表示升级加速比,可改进部件原先运行速度与改进后的部件速度的比值,取值范围是S ≥ 1。

[3]. Slatency(s):指的是程序在S个处理器相对在单个处理器(单核)中速度提升比率。一般用作表示整个任务的提速比。

根据这个公式,我们可以依据可确定程序中可并行代码的比例,来决定我们实际工作中增加处理器(总核心数)所能带来的速度提升的上限。

无论是C++开发者在Linux系统中使用的pthread,还是Java开发者使用的java.util.concurrent(JUC)库,这些线程机制的都需要一定的线程I/O模型来做理论支撑。

所以,接下来,我们就让我们一起探讨和揭开Java领域中的线程I/O模型的神秘面纱,针对那些盘根错落的枝末细节,才能让我们更好地了解和正确认识ava领域中的线程机制。

2|0关健术语

本文用到的一些关键词语以及常用术语,主要如下:

  • 阿姆达尔定律(Amdahl 定律): 用于确定并发系统中性能瓶颈部件在采用措施提示性能后,此部件对系统性能提示的改进程度,即系统加速比。
  • 任务(Task): 表示一个程序需要被完成工作内容,与线程非一对一对应的关系,是一个相对概念。
  • 并发(Concurrent): 表示至少一个任务或者若干 个任务同一个时间段内被执行,但是不是顺序执行,大多数都是以交替的方式被执行。
  • 并行(Parallel): 表示至少一个任务或者若干 个任务同一个时刻被执行。主要是指一个并行连接通过多个通道在同一时间内传播多个数据流。
  • 串行(Serial): 表示至多一个任务或者只有一个 个任务同一个时刻被执行。主要是指在同一时间内只连接传输一个数据流。
  • 内核线程(Kernel Thread): 表示由内核管理的线程,处于操作系统内核空间。用户应用程序通过API和系统调用(system call)来访问线程工具。
  • 应用线程(Application Thread): 表示不需要内核支持而在用户应用程序中实现的线程,处于应用程序空间,也称作用户线程。主要是由JVM管理的线程和JVM自己携带的JVM线程。
  • 上下文切换(Context Switch): 一般是指任务切换, 或者CPU寄存器切换。当多任务内核决定运行另外的任务时, 它保存正在运行任务的当前状态, 也就是CPU寄存器中的全部内容。这些内容被保存在任务自己的堆栈中, 入栈工作完成后就把下一个将要运行的任务的当前状况从该任务的栈中重新装入CPU寄存器, 并开始下一个任务的运行过程。在Java领域中,线程有生命周期,其上下文信息的保存和恢复的过程。
  • 线程安全(Thread Safe): 一段操作共享数据的代码能够保证同一个时间内被多个线程执行而依然保证其数据的正确性的考量。

3|0基本概述

Java领域中的线程主要分为Java层线程(Java Thread) ,JVM层线程(JVM Thread),操作系统层线程(Kernel Thread)。

对于Java领域中,从一定程度上来说,由于Java程序并不直接运行在Linux系统上,而是运行在JVM(Java 虚拟机)上,而一个JVM实例是一个Linux进程,每一个JVM都是一个独立的“沙盒”,JVM之间相互独立,互不通信。

按照操作系统和应用程序两个层次来说,线程主要可以分为内核线程(Kernel Thread) 和应用线程(Application Thread)。

其中,在Java领域中的线程主要分为Java层线程(Java Thread) ,JVM层线程(JVM Thread),操作系统层线程(Kernel Thread)。

一般来说,我们把应用线程看作更高层面的线程,而内核线程需要向应用线程提供支持。由此可见,内核线程和应用线程之间存在一定的映射关系。

因此,从线程映射关系来看,不同的操作系统可能采用不同的映射方式,我们把这些映射关系称为线程的映射,或者可以说作线程映射理论模型(Thread Mappered Theory Model )。

在Java领域中,对于文件的I/O操作,提供了一系列的I/O功能API,主要基于基于流模型实现。我们把这些流模型的设计,称作为I/O流模型(I/O Stream Model )。

其中,Java对照操作系统内核以及网络通信I/O中的传统BIO来说,提供并支持了NIO和AIO的功能API设计,我们把这些设计,称作为线程I/O参考模型(Thread I/O Reference Model )。

另外,对于NIO和AIO还参考了一定的设计模式来实现,我们把这些基于设计模式的设计,称作为线程设计模式模型(Thread I/O Design Pattern Model )。

综上所述,在Java领域中,我们在学习和掌握Java并发编程的时候,可以按照:线程映射理论模型->I/O流模型->线程I/O参考模型->线程设计模式模型->线程价值模型等脉络来一一进行对比分析。

3|1一. Java 领域中的线程映射理论模型

Java 领域中的线程映射模型主要有内核级线程模型(Kernel-Level Thread ,KLT)、应用级线程模型(Application-Level Thread ,ALT)、混合两级线程模型(Mixture-Level Thread ,MLT)等3种模型。

从Java线程映射类型来看,主要有线程一对一(1:1)映射,线程多对多(M:1)映射,线程多对多(M:N)映射等关系。

对应到线程模型来说,线程一对一(1:1)映射对应着内核线程(Kernel-Level Thread ,KLT),线程多对多(M:1)映射对应着应用级线程(Application-Level Thread,ALT),线程多对多(M:N)映射对应着混合两级线程(Mixture-Level Thread ,MLT)。

因此,Java领域中实现多线程主要有3种模型:内核级线程模型、应用级线程模型、混合两级线程模型。它们之间最大的差异就在于线程与内核调度实体( Kernel Scheduling Entity,简称KSE)之间的对应关系上。

顾名思义,内核调度实体就是可以被内核的调度器调度的对象,因此称为内核级线程,是操作系统内核的最小调度单元。

综上所述,接下来,我们来详细讨论Java 领域中的线程映射理论模型。

1. 应用级线程模型

应用级线程模型主要是指(Application-Level Thread ,ALT),就是多个用户线程映射到同一个内核线程上,用户线程的创建、调度、同步的所有操作全部都是由用户空间的线程来完成的。

在Java领域中,应用级线程主要是指Java语言编写应用程序的Java 线程(Java Thread)和JVM虚拟机中JVM线程(JVM Thread)。

在应用级线程模型下,完全建立在用户空间的线程库上,不依赖于系统内核,用户线程的创建、同步、切换和销毁等操作完全在用户态执行,不需要切换到内核态。

其中,用户进程使用系统内核提供的接口——轻量级进程(Light Weight Process,LWP)来使用系统内核线程。

在此种线程模型下,由于一个用户线程对应一个LWP,因此某个LWP在调用过程中阻塞了不会影响整个进程的执行。

但是各种线程的操作都需要在用户态和内核态之间频繁切换,消耗太大,速度相对用户线程模型来说要慢。

2. 内核级线程模型

内核级线程模型主要是指(Kernel-Level Thread ,KLT),用户线程与内核线程建立了一对一的关系,即一个用户线程对应一个内核线程,内核负责每个线程的调度。

在Linux中,对于内核级线程,操作系统会为其创建一套栈:用户栈+内核栈,其中用户栈工作在用户态,内核栈工作在内核态,在发生系统调用时,线程的执行会从用户栈切换到内核栈。

在内核级线程模型下,完全依赖操作系统内核提供的内核线程来实现多线程。线程的切换调度由系统内核完成,系统内核负责将多个线程执行的任务映射到各个CPU中去执行。

其中,glibc中的pthread_create方法主要是创建一个OS内核级线程,我们不深入细节,主要是为该线程分配了栈资源;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值