前言

Java语言的特点之一就是天然支持并发编程,且并发编程也是Java开发需要掌握的最重要的技能之一(面试重点)也是比较难掌握的技能之一。这个系列会对Java并发编程做一次总结,希望可以帮助到一些人,也是对我自己知识掌握情况的总结和回顾,如有错误,请及时指正!!

在学习并发编程之前,这两个概念肯定是绕不开的,这是实现并发编程的基础,所以,在学习Java并发编程之前,让我们先来看看,究竟什么是进程、线程和协程,以及他们和Java并发编程究竟有什么关系。

1、什么进程?

首先,我们的计算机是使用CPU进行计算的,CPU是计算机的核心。但是计算机并不只有CPU,还包括其他许多资源,如内存、外存等等,一个计算任务往往需要涉及到许多计算机硬件,并且通常我们可能有很多个任务,但是一台计算机的硬件资源是有限的,于是我们需要一个程序对这些计算机硬件进行统一的管理和调度这些任务,这个程序就是我们所说的操作系统。

操作系统 管理着整个计算机硬件,负责任务的调度、资源的分配和管理。

而由我们程序员编写的应用程序,则是运行在操作系统之上,我们的程序需要的资源,也由操作系统进行分配。

通俗地说,应用程序就可以理解为是一个进程,如一个运行在操作系统上的微信程序等等。但是作为专业的程序员,我们应该要有一个专业的说法。

进程是一个具有一定独立功能的程序在一个数据集上的一次动态执行的过程,是操作系统进行资源分配和调度的一个独立单位,是应用程序运行的载体。

这句话看起来可能有点不好理解,我们可以拆开来理解。

  • 具有一定独立功能的程序:独立功能,说明每个线程都有自己的任务,线程与线程之间是互不干扰的。程序就是由程序员编写的应用程序,描述了这个进程要完成的任务,通常是高级编程语言,最终都会经过编译或解释编程机器语言,也就是控制进程执行的指令集。
  • 一个数据集上的一次动态执行的过程:数据集就是程序在执行时所需要的数据和工作空间,就是操作系统为进程分配的相关资源。

总结:进程是程序的一次执行过程,是操作系统分配资源的最小单位,每个进程都是独立的,有自己的数据集和工作空间,互不干扰。

2、什么是线程?和进程的关系是什么?

首先一句话概括,线程是程序执行中一个单一的顺序控制流程,是程序执行流的最小单元,是处理器调度和分派的基本单位。一个进程包含一个或多个线程,多个线程共享该进程的资源,如内存空间。

2.1 线程

一个线程,通常有自己的线程ID(线程的身份证)、当前指令指针(PC)、寄存器和堆栈组成。

线程是程序执行流的最小单元,也就是CPU执行任务和调度的最小单元。

2.2 线程和进程的关系

一个进程通常有一个或多个线程组成,操作系统为进程分配资源,而这些线程则共享该进程的内存空间。

每个进程间是相互独立的,但是在统一进程下的这些线程并不是,他们共享了内存空间还有一些进程级的资源,如打开文件和信号。

由于线程是属于进程的,所以,不同进程内的线程也是相互独立的。

java mc处理器 java开发cpu_java mc处理器

另外,线程的上下文切换要比进程块的多。

3、什么是多线程并发编程?

先搞清楚两个概念,并发并行

  • 并发:同一时间段内多个任务同时执行。
  • 并行:单位时间内,多个任务在同时执行。

所以,并发任务强调在一个时间段内同时执行,而一个时间段由多个单位时间累积而成,所以说并发的多个任务在单位时间内不一定同时在执行。

在单CPU情况下,两个线程需要接受操作系统的调度,轮流在CPU上完成任务,并且并不是一直到自己的任务结束才从CPU离开,每个线程都有自己的时间片,用完了就得离开,并且记录自己离开前程序执行的状态,在下次得到新的执行权限后,根据保存的信息继续执行,这就是上下文切换

只有在多核计算机中,才存在真正的并行。

而在多线程编程实践中,线程的个数往往多于CPU的个数,所以一般都称多线程并发编程而不是多线程并行编程。

多核CPU时代的到来打破了单核CPU对多线程效能的限制。多个CPU意味着每个线程可以使用自己的CPU运行,这减少了线程上下文切换的开销,但随着对应用系统性能和吞吐量要求的提高,出现了处理海量数据和请求的要求,这些都对高并发编程有着迫切的需求。

4、多核与多线程

前文说过,线程的个数往往是要多与CPU的个数的,那么,这些任务在有限的CPU中是如何分配的?多核CPU究竟是什么?他和多线程之间的关系又是什么?

4.1 多核

多核处理器是指在一个处理器上集成多个运算核心从而提高计算能力,每一个处理核心对应一个内核线程

内核线程(Kernel Thread,KLT)就是直接由操作系统内核支持的线程,这种线程由内核来完成线程切换,内核通过操作调度器对线程进行调度,并负责将线程的任务映射到各个处理器上。

我们现在所说的双核或者四核,对应的就是内核线程的个数,双核就是两个内核线程,四核就是四个。

但是现在的电脑一般是双核四线程,这就是采用超线程技术将一个物理处理核心模拟成两个逻辑处理核心,对应两个内核线程,所以在操作系统中看到的CPU数量是实际物理CPU数量的两倍。超线程技术就是利用特殊的硬件指令,把一个物理芯片模拟成两个逻辑处理核心,让单个处理器都能使用线程级并行计算,进而兼容多线程操作系统和软件,减少了CPU的闲置时间,提高的CPU的运行效率。这种超线程技术(如双核四线程)由处理器硬件的决定,同时也需要操作系统的支持才能在计算机中表现出来。

程序一般不会直接去使用内核线程,而是去使用内核线程的一种高级接口——轻量级进程(Lightweight Process,LWP),轻量级进程就是我们通常意义上所讲的线程,也被叫做用户线程

4.2 任务调度

那么,无限的线程如何在有限的CPU上工作呢,这就需要操作系统对于任务的调度了。如今大部分操作系统(如Windows、Linux)的任务调度是采用时间片轮转的抢占式调度方式。

4.3 Java中线程的实现

java 的线程是通过 native方法实现的,实现线程的主要 3 种方式:使用内核线程实现,使用用户线程实现以及使用用户线程加轻量级进程混合实现。

  • 内核线程实现:
    轻量级进程与内核线程之间 1:1 的对应关系。

java mc处理器 java开发cpu_java mc处理器_02

这种实现方式的优点是内核直接支持,由操作系统内核创建和撤销。内核维护进程及线程的上下文信息以及线程切换。一个内核线程由于 I/O 操作而阻塞,不会影响其它线程的运行。

但是他也存在缺点:

1、线程的操作、创建、同步等都需要系统调用,而系统调用代价比较高,需要在用户态和内核态中来回切换。

2、每个轻量级的进程都需要一个内核线程来支持,需要消耗一定的内核资源。

  • 用户线程实现
    用户线程指不需要内核支持而在用户程序中实现的线程,其不依赖于操作系统核心,应用进程利用线程库提供创建、同步、调度和管理线程的函数来控制用户线程。
    不需要用户态 / 核心态切换,速度快,操作系统内核不知道多线程的存在,因此一个线程阻塞将使得整个进程(包括它的所有线程)阻塞。使用用户线程实现的程序一般都比较复杂,java 曾经用过,不过最后还是放弃了。
  • 用户线程加轻量级进程混合实现
    这种混合模式下,既存在用户线程,也存在轻量级进程。用户线程还是完全建立在用户空间中,因此用户线程的创建、切换、析构等依然廉价,可以支持大规模的用户线程并发。
    操作系统提供支持的轻量进程作为用户线程和内核线程之间的桥梁,用户线程的系统调用要通过轻量级线程来完成,大大降低了进程阻塞的风险。用户线程和轻量级进程比是 N:M 多对对的关系。
  • java mc处理器 java开发cpu_经验分享_03