什么是线程?什么是进程?为什么要有线程?有什么关系与区别?
进程:
- 程序执行时的一个实例
- 每个进程都有独立的内存地址空间
- 系统进行资源分配和调度的基本单位
- 进程里的堆,是一个进程中最大的一块内存,被进程中的所有线程共享的,进程创建时分配,主要存放 new 创建的对象实例
- 进程里的方法区,是用来存放进程中的代码片段的,是线程共享的
- 在多线程 OS 中,进程不是一个可执行的实体,即一个进程至少创建一个线程去执行代码
为什么要有线程?
每个进程都有自己的地址空间,即进程空间。一个服务器通常需要接收大量并发请求,为每一个请求都创建一个进程系统开销大、请求响应效率低,因此操作系统引进线程。
线程:
- 进程中的一个实体
- 进程的一个执行路径
- CPU 调度和分派的基本单位
- 线程本身是不会独立存在
- 当前线程 CPU 时间片用完后,会让出 CPU 等下次轮到自己时候在执行
- 系统不会为线程分配内存,线程组之间只能共享所属进程的资源
- 线程只拥有在运行中必不可少的资源(如程序计数器、栈)
- 线程里的程序计数器就是为了记录该线程让出 CPU 时候的执行地址,待再次分配到时间片时候就可以从自己私有的计数器指定地址继续执行
- 每个线程有自己的栈资源,用于存储该线程的局部变量和调用栈帧,其它线程无权访问
关系:
- 一个程序至少一个进程,一个进程至少一个线程,进程中的多个线程是共享进程的资源
- Java 中当我们启动 main 函数时候就启动了一个 JVM 的进程,而 main 函数所在线程就是这个进程中的一个线程,也叫做主线程
- 一个进程中有多个线程,多个线程共享进程的堆和方法区资源,但是每个线程有自己的程序计数器,栈区域
如下图
区别:
- 本质:进程是操作系统资源分配的基本单位;线程是任务调度和执行的基本单位
- 内存分配:系统在运行的时候会为每个进程分配不同的内存空间,建立数据表来维护代码段、堆栈段和数据段;除了 CPU 外,系统不会为线程分配内存,线程所使用的资源来自其所属进程的资源
- 资源拥有:进程之间的资源是独立的,无法共享;同一进程的所有线程共享本进程的资源,如内存,CPU,IO 等
- 开销:每个进程都有独立的代码和数据空间,程序之间的切换会有较大的开销;线程可以看做轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行程序计数器和栈,线程之间切换的开销小
- 通信:进程间 以IPC(管道,信号量,共享内存,消息队列,文件,套接字等)方式通信 ;同一个进程下,线程间可以共享全局变量、静态变量等数据进行通信,做到同步和互斥,以保证数据的一致性
- 调度和切换:线程上下文切换比进程上下文切换快,代价小
- 执行过程:每个进程都有一个程序执行的入口,顺序执行序列;线程不能够独立执行,必须依存在应用程序中,由程序的多线程控制机制控制
- 健壮性:每个进程之间的资源是独立的,当一个进程崩溃时,不会影响其他进程;同一进程的线程共享此线程的资源,当一个线程发生崩溃时,此进程也会发生崩溃,稳定性差,容易出现共享与资源竞争产生的各种问题,如死锁等
- 可维护性:线程的可维护性,代码也较难调试,bug 难排查
进程与线程的选择:
- 需要频繁创建销毁的优先使用线程。因为进程创建、销毁一个进程代价很大,需要不停的分配资源;线程频繁的调用只改变 CPU 的执行
- 线程的切换速度快,需要大量计算,切换频繁时,用线程
- 耗时的操作使用线程可提高应用程序的响应
- 线程对 CPU 的使用效率更优,多机器分布的用进程,多核分布用线程
- 需要跨机器移植,优先考虑用进程
- 需要更稳定、安全时,优先考虑用进程
- 需要速度时,优先考虑用线程
- 并行性要求很高时,优先考虑用线程
Java 编程语言中线程是通过 java.lang.Thread 类实现的。
Thread 类中包含 tid(线程id)、name(线程名称)、group(线程组)、daemon(是否守护线程)、priority(优先级) 等重要属性。