Java线程模型

Java线程模型

JAVA大行其道的今天,已经距离JAVA产生已经二十多年,并不意味着JAVA就已经尽善尽美了,他依旧存在不足与值得思考的地方

线程三大模型

线程通常被定义为一个进程中代码的不同执行路线,一个进程可包含多个线程。从实现方式上划分,线程有两种类型:“用户级线程”和“内核级线程”(kernel-level treads)。而线程模型就是根据内核线程和用户线程之间的映射关系区分出来的。

内核线程

指需要内核的参与,由内核完成线程的调度。其依赖于操作系统核心,由内核的内部需求进行创建和撤销。内核维护进程及线程的上下文信息以及线程切换。内核线程的线程表(thread table)位于内核中,包括了线程控制快(TCB),一旦线程阻塞,内核会从当前或者其他进程(process)中重新选择一个线程保证程序的执行。

多核(心)处理器是指在一个处理器上集成多个运算核心从而提高计算能力,也就是有多个真正并行计算的处理核心,每一个处理核心对应一个内核线程。一般一个处理核心对应一个内核线程,比如单核处理器对应一个内核线程,双核处理器对应两个内核线程,四核处理器对应四个内核线程。(除了部分拥有超线程的处理器)

用户线程

指不需要内核支持而在用户程序中实现的线程,其不依赖于操作系统核心,应用进程利用线程库提供创建、同步、调度和管理线程的函数来控制用户线程。

N:1模型

用户线程与内核线程是多对一(N : 1)的映射模型,多个用户线程的一般从属于单个进程并且多线程的调度是由用户自己的线程库来完成,线程的创建、销毁以及多线程之间的协调等操作都是由用户自己的线程库来负责而无须借助系统调用来实现。

在这里插入图片描述

一个进程中所有创建的线程都只和同一个内核线程在运行时动态绑定,也就是说,操作系统只知道用户进程而对其中的线程是无感知的,内核的所有调度都是基于用户进程。许多语言实现的 协程库 基本上都属于这种方式(比如python的gevent)。
由于线程调度是在用户层面完成的,也就是相较于内核调度不需要让CPU在用户态和内核态之间切换,这种实现方式相比内核级线程可以做的很轻量级,对系统资源的消耗会小很多,因此可以创建的线程数量与上下文切换所花费的代价也会小得多。
但该模型有个原罪:当多线程并发执行时,如果其中一个线程执行IO操作时,内核接管这个操作,如果IO阻塞,用户态的其他线程都会被阻塞,因为这些线程都对应同一个内核调度实体。在多处理器机器上,内核不知道用户态有这些线程,无法把它们调度到其他处理器,也无法通过优先级来调度。这对线程的使用是没有意义的!

1:1模型

用户线程与内核线程内核线程是一对一(1 : 1)的映射模型,也就是每一个用户线程绑定一个实际的内核线程,而线程的调度则完全交付给操作系统内核去做,应用程序对线程的创建、终止以及同步都基于内核提供的系统调用来完成。

在这里插入图片描述

大部分编程语言的线程库(比如Java的java.lang.Thread、C++11的std::thread等等)都是对操作系统的线程(内核级线程)的一层封装,创建出来的每个线程与一个独立的内核线程静态绑定,因此其调度完全由操作系统内核调度器去做。

所以java线程如果出现死锁,或者饥饿会导致系统很大开销,因为他使用native调用系统调用,fork出来的内核线程

这种模型的优势和劣势同样明显:优势是实现简单,直接借助操作系统内核的线程以及调度器,所以CPU可以快速切换调度线程,于是多个线程可以同时运行,因此相较于用户级线程模型它真正做到了并行处理;但它的劣势是,由于直接借助了操作系统内核来创建、销毁和以及多个线程之间的上下文切换和调度,因此资源成本大幅上涨,且对性能影响很大。

混合模型(N:N)

多路复用多个用户级线程到同样数量或更少数量的内核线程。内核线程的数量可能与特定应用程序或特定机器有关(应用程序在多处理器上比在单处理器上可能分配到更多数量的线程)。

在这里插入图片描述

混合模型是博采众长之后的产物,充分吸收前两种线程模型的优点且尽量规避它们的缺点。在此模型下,用户线程与内核线程是多对多(N : M)的映射模型:
首先,区别于用户级线程模型,混合模型中的一个进程可以与多个内核线程内核线程关联,于是进程内的多个线程可以绑定不同的内核线程,这点和内核级线程模型相似;
其次,又区别于内核级线程模型,它的进程里的所有线程并不与内核线程一一绑定,而是可以动态绑定同一个内核线程, 当某个内核线程因为其绑定的线程的阻塞操作被内核调度出CPU时,其关联的进程中其余用户线程可以重新与其他内核线程绑定运行。
所以,混合模型既不是用户级线程模型那种完全靠自己调度的也不是内核级线程模型完全靠操作系统调度的,而是中间态(自身调度与系统调度协同工作),因为这种模型的高度复杂性,而且并不是所有操作系统都支持多对多的混合模型,操作系统内核开发者一般不会使用,(Linux 的 Native POSIX Threads Library 就因为其复杂性pass掉了这种实现方式, 但是 Solaris JDK 又实现了在Solaris 下的混合多对多模型),所以更多时候是作为第三方库的形式出现,而Go语言中的runtime调度器就是采用的这种实现方案。

JAVA中的线程实现模型

1:1模型

Callable 非阻塞模型,但是内核线程依旧阻塞占用CPU时间片,完成后回调上层
CompletableFuture 非阻塞模型,实现了Future,多线程阻塞占用CPU时间片
Future 非阻塞模型,但是内核线程依旧阻塞占用CPU时间片,需要轮询线程是否完成

多对多模型

ForkJoin 分而治之,建立多个子任务,交付ForkJoinPool执行
Rxjava , AKKa 基于消息的多对多模型

多对一模型

Kotlin,wisp(AliJVM),Quasar 将内核线程抽象为多个协程进行有效调度

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值