很多程序员对于线程和进程还不是很清楚。尤其是在面试官问到 Redis 的知识的时候,很容易掉进面试陷阱。
其实我在 3 年前也写过一篇《线程和进程》。现在还有很多网友问起我,我这里再做一篇分享。
进程
我们都知道计算机的核心是CPU,它承担了所有的计算任务;而操作系统是计算机的管理者,它负责任务调度、资源分配管理,统领整个计算机硬件;应用程序是具有某种功能的程序,程序是运行于操作系统之上的。
进程是一个具有一定独立功能的程序在一个数据集上的一次动态执行的过程,是操作系统进行资源分配和调度的一个独立单位,是应用程序运行的载体。进程是一种抽象的概念,从来没有统一的标准定义。进程一般由程序、数据集合和进程控制块三部分组成。程序用于描述进程要完成的功能,是控制进程执行的指令集;数据集合是程序在执行时所需要的数据和工作区;程序控制块(Program Control Block,简称PCB),包含进程的描述信息和控制信息,是进程存在的唯一标志。
进程具有的特征:
- 动态性:进程是程序的一次执行过程,是临时的,有生命期的,是动态产生,动态消亡的;
- 并发性:任何进程都可以同其他进程一起并发执行;
- 独立性:进程是系统进行资源分配和调度的一个独立单位;
- 结构性:进程由程序、数据和进程控制块三部分组成。
线程
线程是程序执行中一个单一的顺序控制流程,是程序执行的最小单元,是CPU调度和分派的基本单位。一个进程可以有一个或多个线程,各个线程之间共享程序的内存空间(也就是所在进程的内存空间)。一个标准的线程由线程ID、当前指令指针(PC)、寄存器和堆栈组成。而进程由内存空间(代码、数据、进程空间、打开的文件)和一个或多个线程组成。
进程终止的情况
- 程序运行到最后,执行完所有的指令,便会结束.
- 程序运行到显示使用System.exit()或Runtime,getRuntime().exit.退出当前进程.
- 程序执行过程中遇到未捕获的异常或者错误而结束.
- 程序所在的平台强制结束JVM的运行.
定义
- 进程:具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位.
- 线程:进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源.
jvm不区分主线程和用户线程,各个线程是独立的(平级关系),不同与win32的线程模型。jvm把线程分为前台线程和后台线程,后台线程官方术语叫守护线程,jvm结束的条件是所有的前台线程结束。正常构建的线程都是前台线程,可以在线程未开始前调用Thread类的setDaemon(true)方法将线程改变为后台守护线程。
情况1:正常情况下,主线程启动了子线程,主线程、子线程各自执行,彼此不受影响。
当你在run一个Java application的时候,这个时候系统会开一个进程。然后这个进程启动了Main线程。Java进程确定虚拟机中没有线程运行的时候,退出进程。或者也可以用System.exit(0);强制退出进程
按照操作系统的理论,进程是资源分配的基本单位,线程是CPU调度的基本单位。对于CPU来说,其实并不存在java的主线程和子线程之分,都只是个普通的线程。进程的资源是线程共享的,只要进程还在,线程就可以正常执行,换句话说线程是强依赖于进程的。也就是说,线程其实并不存在互相依赖的关系,一个线程的死亡从理论上来说,不会对其他线程有什么影响。
情况2:需求是主线程执行结束,由主线程启动的子线程都结束
在子线程未开始前调用Thread类的setDaemon(true)方法将子线程改变为后台守护线程,这样主线程执行完毕后会立即杀死所以的后台守护线程
java虚拟机(相当于进程)退出的时机是:虚拟机中所有存活的线程都是守护线程。只要还有存活的非守护线程虚拟机就不会退出,而是等待非守护线程执行完毕;反之,如果虚拟机中的线程都是守护线程,那么不管这些线程的死活java虚拟机都会退出。这是属于JVM的底层实现机制,并不是说主线程和子线程之间存在依赖关系。这是属于JVM的底层实现机制,并不是说主线程和子线程之间存在依赖关系。
情况3:需求是子线程执行结束,主线程等待启动的子线程都结束之后再结束
在子线程开始后调用Thread类的join()方法,加入子线程,等待其结束,这样主线程执行完毕后会立即杀死所以的后台守护线程
进程和线程的区别
- 进程是操作系统进行资源的分配和调度的一个独立单元,而线程是CPU调度的基本单元
- 同一个进程中可以包括多个线程,并且线程共享整个进程的资源(寄存器、堆栈、上下文),一个进程至少包括一个线程。
- 进程的创建调用fork或者vfork,而线程的创建调用pthread_create,进程结束后它拥有的所有线程都将销毁,而线程的结束不会影响同个进程中的其他线程的结束
- 线程是轻量级的进程,它的创建和销毁所需要的时间比进程小很多,所有操作系统中的执行功能都是创建线程去完成的
- 线程中执行时一般都要进行同步和互斥,因为他们共享同一进程的所有资源
- 线程有自己的私有属性TCB,线程id,寄存器、硬件上下文,而进程也有自己的私有属性进程控制块PCB,这些私有属性是不被共享的,用来标示一个进程或一个线程的标志
死锁?死锁产生的原因?死锁的必要条件?怎么处理死锁?
- 相互等待资源而产生的一种僵持状态,如果没有外力的干预将一直持续这个状态
- 系统资源不足、相互竞争资源、请求资源顺序不当
- 互斥、不可抢占、循环等待、请求与保持
- 因为互斥是不可改变的,所以只能破坏其他三个条件中的一个来解除死锁,方法:剥夺资源、杀死其中一个线程
Windows内存管理方式
段存储、页存储、段页存储。
进程的几种状态?
- run(运行状态):正在运行的进程或在等待队列中对待的进程,等待的进程只要以得到cpu就可以运行
- Sleep(可中断休眠状态):相当于阻塞或在等待的状态
- D(不可中断休眠状态):在磁盘上的进程
- T(停止状态):这中状态无法直观的看见,因为是进程停止后就释放了资源,所以不会留在linux中
- Z(僵尸状态):子进程先与父进程结束,但父进程没有调用wait或waitpid来回收子进程的资源,所以子进程就成了僵尸进程,如果父进程结束后任然没有回收子进程的资源,那么1号进程将回收
IPC 通信方式?
- 管道(匿名管道(pipe亲缘关系的进程通信)、命名管道(mkfifo/mknod))
- 消息队列:是基于消息的、用无亲缘关系的进程间通信,主要函数:msgget、msgsend、msgrecv、msgctl
- 信号量:相当于一把互斥锁,通过p、v操作,主要函数:semget、semop、semctl
- 共享内存:是进程间通信速度最快的,所以用经常是集合信号量或互斥锁来实现同步,shmget、shmat、shmdt、shmctl
什么是虚拟内存?
是将进程部分装入内存中,从而能实现一个很大的程序能在一个比它小的内存中运行,它的主要实现是靠程序的换进换出来实现的,因为内存中0~3G是用户使用,3~4G才是内存使用,通过映射来实现来进行逻辑地址到物理地址的映射。
虚拟地址、逻辑地址、线性地址、物理地址的区别?
分段机制把一个逻辑地址转换为线性地址;接着,分页机制把一个线性地址转换为物理地址。
- 虚拟地址:虚拟内存映射出来的地址
- 逻辑地址:程序的段加偏移量形成的,C/C++程序中取地址求出来的地址就是逻辑地址
- 线性地址:是逻辑地址到物理地址的中间层,只有启动分页机制的时候才有线性地址,如果没有分页机制,那么线性地址就是物理地址
- 物理地址:是内存中实实在在存在的硬件地址,逻辑地址(启动分段)–>线性地址(启动分页)–>物理地址
这些知识都比较理论,但是在实际应用中经常会遇到。如果你连理论知识都搞不清,遇到实际问题,那就无从下手,越搞越错了。
: » JVM 进程、线程
原创文章,作者:Carrie001128,如若转载,请注明出处:https://blog.ytso.com/251758.html