综合了​​进程与线程概念​​和​​进程和线程的区别(超详细)​​两篇博客,两位大佬都写的很好,很仔细,我这里综合一下。

进程:

进程的概念

进程是指一个具有一定独立功能的程序,在一个数据集合上的一次动态执行过程,一个在内存中运行的应用程序。每个进程都有自己独立的一块内存空间,一个进程可以有多个线程,比如在Windows系统中,一个运行的xx.exe就是一个进程。

进程与线程的概念以及区别_java


如下图所示,源代码文件经过编译链接形成可执行文件,将可执行文件加载进内存进行执行。为什么要加载进内存呢?这是因为负责解析和运行程序的CPU需要通过内部的程序计数器来指定内存地址,然后才能读出程序;并且,加载进内存,能对数据进行处理,让程序能够运行。那CPU不能直接读取外部磁盘的数据和程序吗?可以。但是访问磁盘的速度远远低于访问内存,速度很慢。

进程与线程的概念以及区别_并发_02


进程就体现了,计算机在执行程序,处理数据时,操作系统是如何对这个过程进行维护的。

进程包含了正在运行的一个程序的所有状态信息:
代码;数据;通用寄存器;进程占用系统资源(打开文件,已分配内存等), 组成进程控制块。

进程的特点

  • 动态性:可动态的创建,结束进程
  • 并发性:进程可以被独立调度并占用CPU
  • 独立性:不同进程的工作互不影响
  • 制约性:因访问共享数据/资源或进程间同步而产生制约

进程是操作系统中处于执行状态程序的抽象。
程序 = 文件(静态的可执行文件)
进程 = 执行中的程序 = 程序 + 执行状态
同一个程序的多次执行过程对应为不同的进程。

进程执行需要的资源:

  1. 内存
  2. CPU

进程与程序的区别:

进程的动态的,程序是静态的
进程是暂时的,程序是永久的
进程与程序的组成不同,进程包括程序,数据和进程控制块

进程的结构

程序,数据和进程控制块

这里详细介绍一下进程控制块
进程控制块:(PCB,process control block)
操作系统管理控制进程运行所用的信息集合。
操作系统用PCB来描述进程的基本情况以及运行变化的过程
PCB是进程存在的唯一标识(每个进程在操作系统中都有一个对应的PCB)

进程创建时生成PCB,进程中止时回收PCB,进程的组织管理通过对PCB的组织管理来实现。

PCB具体包含了哪些信息呢?
进程标识信息,处理机现场保护,进程控制信息
(PC,SP,寄存器,PID,UID,调度优先级,打开文件列表)
控制信息:
调度和状态信息(调度进程和处理机使用情况)
进程间通信信息(进程间通信相关的标识)
存储管理信息(指向进程映像存储空间数据结构)
进程所用资源(进程使用的系统资源,如打开的文件等)
有关数据结构连接信息(与PCB相关的进程队列)

怎么组织进程控制块?

  • 链表:同一状态的进程PCB组成一个链表,多个状态对应不同的链表(就绪链表,阻塞链表。。。)
  • 索引表:同一状态的进程归入一个索引表(由索引指向PCB)多个状态对应多个不同的索引表(指向PCB的指针放入表中)

进程的生命周期:

进程创建,进程执行,进程等待,进程抢占,进程唤醒,进程结束

进程状态转换:

(三状态进程模型)

进程与线程的概念以及区别_多线程_03

(挂起进程模型)

处于挂起状态的进程映像在磁盘上,目的是减少进程占用内存。

进程与线程的概念以及区别_程序计数器_04

线程

为啥会有线程这种东西?
因为有并发进行并且共享数据的这种需求;进程不能满足这种需求吗?进程可以!但是进程来做这样的事情的话,它的开销很大(创建进程,进程结束,进程切换… …)。

多线程的解决思路:

在进程内部增加一类实体,满足以下特性:

  • 实体之间可以并发执行
  • 实体之间共享相同的地址空间

线程的概念:

线程是进程的一部分,是进程中的一个执行任务(控制单元),描述指令流执行状态,负责当前进程中程序的执行。它是进程中的指令执行流的最小单位,是CPU调度的基本单位。
一个进程至少有一个线程,一个进程可以运行多个线程,多个线程可共享数据。

这个时候,进程就是资源分配的角色:进程由一组相关资源构成,包括地址空间(代码段,数据段),打开的文件等资源

与进程不同的是同类的多个线程共享进程的堆和方法区资源,但每个线程有自己的程序计数器、虚拟机栈和本地方法栈,所以系统在产生一个线程,或是在各个线程之间作切换工作时,负担要比进程小得多,也正因为如此,线程也被称为轻量级进程。

进程与线程的概念以及区别_程序计数器_05

线程的特点

线程 = 进程 - 共享资源
线程优点:

  1. 一个进程中可以同时存在多个线程。
  2. 各个线程之间可以并发的执行。
  3. 各个线程之间可以共享地址空间和文件等资源

线程缺点:

  • 一个线程崩溃,会导致其所属进程的所有线程崩溃

线程的实现方式:

  1. 用户线程:在用户空间实现

POSIX Pthreads,Mach C-threads,Solaris threads
由一组用户级的线程库函数来完成线程的管理,包括线程的创建,终止,同步和调度等
特征:不依赖于操作系统内核(内核不了解用户线程的存在;可用于不支持线程的多进程操作系统)。在用户空间实现的线程机制(每个进程有私有的线程控制块列表;TCB由线程库函数维护)。同一进程内的用户线程切换速度快(无需用户态/内核态的切换)。允许每个进程拥有自己的线程调度算法
缺点:线程发起系统调用而阻塞时,整个进程进入等待;不支持基于线程的处理机抢占;只能按进程分配CPU时间

  1. 内核线程:在内核中实现

Windows,Solaris,Linux
进程由内核通过系统调用实现的线程机制,由内核完成线程的创建,终止和管理(内核中PCB中有指针指向线程控制块TCB)
特征:由内核维护PCB和TCB。线程执行系统调用而被阻塞不影响其他线程。线程的创建,终止和切换开销相对较大(通过系统调用/内核函数,在内核实现)。以线程为单位进行CPU时间分配(多线程的进程可获得更多的CPU时间)

  1. 轻量级进程:在内核中实现,支持用户线程

Solaris (LightWeight Process)
内核支持的用户线程。一个进程可有一个或多个轻量级进程,每个轻权进程由一个单独的内核线程来支持(Solaris/Linux)

用户线程与内核线程的对应关系:

  1. N个用户线程对应N个内核线程;
  2. N个用户线程对应一个内核线程;
  3. N个用户线程对应M个内核线程

三种当中,现在的操作系统以第一种为主。

Java多线程

Java 程序天生就是多线程程序,我们可以通过 JMX 来看一下一个普通的 Java 程序有哪些线程,代码如下。

public class MultiThread {
public static void main(String[] args) {
// 获取 Java 线程管理 MXBean
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
// 不需要获取同步的 monitor 和 synchronizer 信息,仅获取线程和线程堆栈信息
ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(false, false);
// 遍历线程信息,仅打印线程 ID 和线程名称信息
for (ThreadInfo threadInfo : threadInfos) {
System.out.println("[" + threadInfo.getThreadId() + "] " + threadInfo.getThreadName());
}
}
}

上述程序输出如下(输出内容可能不同,不用太纠结下面每个线程的作用,只用知道 main 线程执行 main 方法即可):

[6] Monitor Ctrl-Break //监听线程转储或“线程堆栈跟踪”的线程
[5] Attach Listener //负责接收到外部的命令,而对该命令进行执行的并且把结果返回给发送者
[4] Signal Dispatcher // 分发处理给 JVM 信号的线程
[3] Finalizer //在垃圾收集前,调用对象 finalize 方法的线程
[2] Reference Handler //用于处理引用对象本身(软引用、弱引用、虚引用)的垃圾回收的线程
[1] main //main 线程,程序入口

从上面的输出内容可以看出:一个 Java 程序的运行是 main 线程和多个其他线程同时运行。

进程与线程的区别总结

线程具有许多传统进程所具有的特征,故又称为轻型进程(Light—Weight Process)或进程元;而把传统的进程称为重型进程(Heavy—Weight Process),它相当于只有一个线程的任务。在引入了线程的操作系统中,通常一个进程都有若干个线程,至少包含一个线程。

  • 根本区别:进程是操作系统资源分配的基本单位,而线程是处理器任务调度和执行的基本单位
  • 资源开销:进程拥有一个完整的资源平台,每个进程都有独立的代码和数据空间(程序上下文),程序之间的切换会有较大的开销;线程可以看做轻量级的进程,只独享指令流执行的必要资源,同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器(PC),线程之间切换的开销小。所以线程能减少并发执行的时间和空间开销,线程的创建时间比进程短;线程的终止时间比进程短;同一进程内的线程切换时间比进程短;由于同一进程的各线程间共享内存各文件资源,可以不通过内核进行直接通信
  • 包含关系:如果一个进程内有多个线程,则执行过程不是一条线的,而是多条线(线程)共同完成的;线程是进程的一部分,所以线程也被称为轻权进程或者轻量级进程。
  • 内存分配:同一进程的线程共享本进程的地址空间和资源,而进程之间的地址空间和资源是相互独立的
  • 影响关系:一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉。所以多进程要比多线程健壮。
  • 执行过程:每个独立的进程有程序运行的入口、顺序执行序列和程序出口。但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制,两者均可并发执行

从 JVM 角度说进程和线程之间的关系(重要)

图解进程和线程的关系

下图是 Java 内存区域,通过下图我们从 JVM 的角度来说一下线程和进程之间的关系。

进程与线程的概念以及区别_多线程_06

从上图可以看出:一个进程中可以有多个线程,多个线程共享进程的堆和方法区 (JDK1.8 之后的元空间)资源,但是每个线程有自己的程序计数器、虚拟机栈 和 本地方法栈。

程序计数器为什么是私有的?

程序计数器主要有下面两个作用:

  1. 字节码解释器通过改变程序计数器来依次读取指令,从而实现代码的流程控制,如:顺序执行、选择、循环、异常处理。
  2. 在多线程的情况下,程序计数器用于记录当前线程执行的位置,从而当线程被切换回来的时候能够知道该线程上次运行到哪儿了。

需要注意的是,如果执行的是 native 方法,那么程序计数器记录的是 undefined 地址,只有执行的是 Java 代码时程序计数器记录的才是下一条指令的地址。

所以,程序计数器私有主要是为了线程切换后能恢复到正确的执行位置。

虚拟机栈和本地方法栈为什么是私有的?

  • 虚拟机栈:每个 Java方法在执行的同时会创建一个栈帧用于存储局部变量表、操作数栈、常量池引用等信息。从方法调用直至执行完成的过程,就对应着一个栈帧在 Java虚拟机栈中入栈和出栈的过程。
  • 本地方法栈:和虚拟机栈所发挥的作用非常相似,区别是: 虚拟机栈为虚拟机执行 Java 方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。 在 HotSpot 虚拟机中和 Java虚拟机栈合二为一。

所以,为了保证线程中的局部变量不被别的线程访问到,虚拟机栈和本地方法栈是线程私有的。

一句话简单了解堆和方法区

堆和方法区是所有线程共享的资源,其中堆是进程中最大的一块内存,主要用于存放新创建的对象 (所有对象都在这里分配内存),方法区主要用于存放已被加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

多进程和多线程区别

  • 多进程:操作系统中同时运行的多个程序
  • 多线程:在同一个进程中同时运行的多个任务

举个例子,多线程下载软件,可以同时运行多个线程,但是通过程序运行的结果发现,每一次结果都不一致。 因为多线程存在一个特性:随机性。造成的原因:CPU在瞬间不断切换去处理各个线程而导致的,可以理解成多个线程在抢CPU资源。

多线程提高CPU使用率

进程与线程的概念以及区别_多线程_07


多线程并不能提高运行速度,但可以提高运行效率,让CPU的使用率更高。但是如果多线程有安全问题或出现频繁的上下文切换时,运算速度可能反而更低。

Java中的多线程

Java程序的进程里有几个线程:主线程,垃圾回收线程(后台线程)等

在 Java 中,当我们启动 main 函数时其实就是启动了一个 JVM 的进程,而 main 函数所在的线程就是这个进程中的一个线程,也称主线程。

Java支持多线程,当Java程序执行main方法的时候,就是在执行一个名字叫做main的线程,可以在main方法执行时,开启多个线程A,B,C,多个线程 main,A,B,C同时执行,相互抢夺CPU,Thread类是java.lang包下的一个常用类,每一个Thread类的对象,就代表一个处于某种状态的线程