进程与线程
进程与线程
1. 进程概述
一般来说,一个进程由程序段、数据段和进程控制块三部分组成。进程的大致结构如图所示:

- 进程的大致结构程序段一般也被称为代码段。代码段是进程的程序指令在内存中的位置,包含需要执行的指令集合;
- 数据段是进程的操作数据在内存中的位置,包含需要操作的数据集合;
- 程序控制块(Program Control Block,PCB)包含进程的描述信息和控制信息,是进程存在的唯一标志。
PCB主要由四大部分组成:
- 进程的描述信息:进程ID和进程名称,进程ID是唯一的,代表进程的身份;进程状态,比如运行、就绪、阻塞;进程优先级,是进程调度的重要依据。
- 进程的调度信息:程序起始地址,程序的第一行指令的内存地址,从这里开始程序的执行;通信信息,进程间通信时的消息队列。
- 进程的资源信息:内存信息,内存占用情况和内存管理所用的数据结构;I/O设备信息,所用的I/O设备编号及相应的数据结构;文件句柄,所打开文件的信息。
- 进程上下文:执行时各种CPU寄存器的值、当前程序计数器(PC)的值以及各种栈的值等,即进程的环境。在操作系统切换进程时,当前进程被迫让出CPU,当前进程的上下文就保存在PCB结构中,供下次恢复运行时使用。
进程是并发执行的,任何进程都可以同其他进程一起执行。在进程内部,代码段和数据段有自己的独立地址空间,不同进程的地址空间是相互隔离的。
Java编写的程序都运行在Java虚拟机(JVM)中,每当使用Java命令启动一个Java应用程序时,就会启动一个JVM进程。在这个JVM进程内部,所有Java程序代码都是以线程来运行的。JVM找到程序的入口点main()方法,然后运行main()方法,这样就产生了一个线程,这个线程被称为主线程。当main()方法结束后,主线程运行完成,JVM进程也随即退出。
2. 线程概述
线程是指“进程代码段”的一次顺序执行流程。
线程是CPU调度的最小单位。一个进程可以有一个或多个线程,各个线程之间共享进程的内存空间、系统资源,进程仍然是操作系统资源分配的最小单位。
Java程序的进程执行过程就是标准的多线程的执行过程。每当使用Java命令执行一个class类时,实际上就是启动了一个JVM进程。理论上,在该进程的内部至少会启动两个线程,一个是main线程,另一个是GC(垃圾回收)线程。实际上,执行一个Java程序后,通过Process Explorer来观察,线程数量远远不止两个,达到了18个之多。一个标准的线程主要由三部分组成,即线程描述信息、程序计数器(Program Counter,PC)和栈内存,如图所示。

在线程的结构中,线程描述信息即线程的基本信息,主要包括:
- 线程ID(Thread ID,线程标识符)。线程的唯一标识,同一个进程内不同线程的ID不会重叠。
- 线程名称。主要是方便用户识别,用户可以指定线程的名字,如果没有指定,系统就会自动分配一个名称。
- 线程优先级。表示线程调度的优先级,优先级越高,获得CPU的执行机会就越大。
- 线程状态。表示当前线程的执行状态,为新建、就绪、运行、阻塞、结束等状态中的一种。
- 其他。例如是否为守护线程等。
在线程的结构中,程序计数器很重要,它记录着线程下一条指令的代码段内存地址。在线程的结构中,栈内存是代码段中局部变量的存储空间,为线程所独立拥有,在线程之间不共享。在JDK 1.8中,每个线程在创建时默认被分配1MB大小的栈内存。栈内存和堆内存不同,栈内存不受垃圾回收器管理。
1 | package com.crazymakercircle.mutithread.basic.create; |
在Java中,执行程序流程的重要单位是“方法”,而栈内存的分配单位是“栈帧”(或者叫“方法帧”)。方法的每一次执行都需要为其分配一个栈帧(方法帧),栈帧主要保存该方法中的局部变量、方法的返回地址以及其他方法的相关信息。当线程的执行流程进入方法时,JVM就会为方法分配一个对应的栈帧压入栈内存;当线程的执行流程跳出方法时,JVM就从栈内存弹出该方法的栈帧,此时方法帧的局部变量的内存空间就会被回收。

3. 区别
- 线程是“进程代码段”的一次顺序执行流程。一个进程由一个或多个线程组成,一个进程至少有一个线程。
- 线程是CPU调度的最小单位,进程是操作系统分配资源的最小单位。线程的划分尺度小于进程,使得多线程程序的并发性高。
- 线程是出于高并发的调度诉求从进程内部演进而来的。线程的出现既充分发挥了CPU的计算性能,又弥补了进程调度过于笨重的问题。
- 进程之间是相互独立的,但进程内部的各个线程之间并不完全独立。各个线程之间共享进程的方法区内存、堆内存、系统资源(文件句柄、系统信号等)。
- 切换速度不同:线程上下文切换比进程上下文切换要快得多。所以,有的时候,线程也称为轻量级进程。