进程:正在运行中的程序(进程是驻留在内存中的)
- 是系统执行资源分配和调度的独立单位
- 每一个进程都有属于自己的存储空间和系统资源
- 注意:进程A 和 进程B 的内存独立不共享
使用jdk自带的工具,jconsole查看当前Java进程中的所有线程。
new Thread对象,操作不创建线程。(说的线程指的是 系统内核里的PCB)
调用start才是创建PCB,才是有货真价实的线程的。
线程:进程中的单个顺序控制流,也可以理解成是一条执行路径。
- 单线程:一个进程中包含一个顺序控制流(一条执行路径)
- 多线程:一个进程中包含多个顺序控制流(多条执行路径)
- 在java中:线程A和线程B,堆内存和方法区内存共享。但是栈内存独立,一个线程一个栈。
- 多线程:为了提高程序的处理效率。
- 对于单核的CPU来说,不能够做到真正的多线程并发,但是可以做到给人一种“多线程并发“的感觉。对于单核的CPU来说,在某一个时间点上实际上只能处理一件事情,但由于CPU的处理速度极快,多个线程之间频繁切换执行,给人的感觉是多个事情同时在做。
PCB(Process Control Block)进程控制块:描述控制进程的运行,系统中存放进程的管理和控制信息和数据结构。
PCB一般包括:
- 进程ID(PID、进程句柄):它是唯一的,一个进程都必须对应一个PID。
- 特征信息:一般分系统进程、用户进程、或者内核进程等。
- 进程状态:运行、就绪、阻塞、表示进程现的运行情况。
- 优先级:表示获得CPU控制权的优先级大小。
- 通信信息:进程之间的通信关系的反映,由于操作系统会提供通信信道。
- 线程保护区:保护阻塞的进程用
- 资源需求、分配控制信息
- 进程实体信息:指明程序路径和名称,进程数据在物理内存还是在交换分区中
- 其他信息:工作单位、工作区、文件信息等。
同一个进程里的若干个PCB pid相同。不同进程的pid是不同的
PCB不是”简称“是一个数据结构,体现的是 进程/线程是如何实现的,如何被描述的。
PCB对应的是线程
一个线程对应多个PCB
如果一个进程只有一个线程,就是一个进程对应一个PCB了。
线程安全问题:罪魁祸首——抢占式执行,随机调度。
本质上还是系统里的线程的封装,每个Thread的对象就对应到系统中的一个线程,也就是PCB
start 和 run 区别:
- start是真正创建了一个线程(从系统这里创建的)
- run 知识描述了线程要干的活是啥,如果直接在main中调用run,此时没有创建新线程,全是main线程一个人干的活。
进程和线程:
进程包含线程,要想看到线程,要先找到对应的进程,再看进程里有哪些线程。
实现线程方法
1、继承Thread,重写 run
接口比抽象类更进一步,抽象类接口则不行,要求方法都是抽象方法。
2、实现Runnable接口 实现一个interface
解耦合,目的就是为了让线程和线程要干的活 之间分离开。
未来改代码,不用多线程,或者线程池,或者协程……此时代码改动较小。
3、使用匿名类,继承Thread
创建了一个Thread的子类。(子类没有名字)所以才叫做匿名。
创建了子类的实例,并且让 t 引用执行实例
4、使用匿名内部类,实现Runnable
这个写法和2本质相同,只不过把实现Runnable任务交给匿名内部类的语法。
此处是创建了一个类,实现了Runnable,同时创建了类的实例,并且传给Thread的构造方法。
5、使用Lambda表达式(最简单,推荐写法)
把任务Lambda表达式来描述
直接把Lambda传给Thread构造方法
(匿名内部类被重写方法的形参列表)-> {
被重写方法的方法体代码
}
lambda 表达式只能简化函数式接口匿名内部类的写法形式,且接口中有且仅有一个抽象方法
上述方法,只是语法规则不同,本质上都是一样的方式,这些方式创建处理啊的线程都是一样的。
抽象类与普通类的区别
- 抽象类不能实例化,不能new
- 必须要搞个子类,来继承抽象类
- 抽象类里还有抽象方法,抽象方法没有方法体,需让子类重写