JavaEE的进程,线程和创建线程的5种方式详解
一、认识进程、线程
1.1什么是进程
进程process/task.“进程"是计算机完成一个工作的"过程”
设备上一个正在运行的程序,就是一个进程。比如你打开的QQ就是一个进程,正在和别人聊天的微信也是一个进程。进程是系统进行资源分配的基本单位。
当我们打开任务管理器就可以看到,当前操作系统中正在运行的进程。
要想让一个进程真正的运行起来,就需要给这个进程分配一定的系统硬件资源。这些资源都包括:
CPU:例如我电脑中任务管理器占用了11%的CPU,QQ占用0.3%。
内存:任务管理器占用了45.7MB。Microsoft Edge占用了320.0MB
磁盘:qq使用的了0.2MB/秒。
网络带宽…
在举一个例子:我是班长,老师想让我组织一个活动,我要想组织这个活动,我就需要向老师申请一些活动经费、人员调用。这里我就相当于进程。老师就是CPU。我组织活动的时候向老师申请经费、人。就是在请求分配一些资源。有经费,有人才能把活动做好。
进程的管理
管理=描述(PCB)+组织
进程的组织:
使用一定的数据结构来组织。常见的做法是用一双向链表。 当你查看进程列表都有哪些进程时,本质上就是遍历操作系统内核中的这个组织进程的链表,再显示出每个进程的这些属性。创建一个进程,本质上就是创建了一个PCB对象,把这个对象加入到内核的链表中。销毁一个进程,本质上就是把这个PCB对象从内核链表中删除。
进程的描述:
PCB描述进程。这个PCB实际上是一个非常大的结构体,属性有很多,例如:PID(下表第二列)、内存指针、文件描述符表、进程的状态、上下文、优先级、记账信息等等。
PID: 一个进程的身份标识,一个机器同一时刻每个进程的PID是唯一的。
内存指针: 描述这个进程使用的内存空间是哪个范围
文件描述附表: 描述这个进程都打开了哪些文件
进程的调度
说到进程,就会涉及到进程的调度,刚才可以看到我电脑上的进程是非常多的,虽然应用那里只显示了5个,但是后台还是帮我运行了87个进程。相信大家的电脑一定没有这么多CPU吧。CPU数目是少于进程数目的,但是我又需要让那些进程“同时执行”。我们的系统是支持多任务的系统。而这个多任务系统其实就是基于进程调度这样的机制来完成的。
并发式执行
举个例子:假设有小张同学,他长的很好看,在学校里有很多的追求者。 按正常男人的标准,我同时只能和一个女生交往~
那小张同学有没有办法做到同时和多个女生交往呢? 小张同学前思后想,最终决定!安排一个时间表!!!
周一早上:和A女生一起吃早饭
周二下午:和B女生一起逛街
周三晚上:和C女生一起看电影
只要小张把时间表安排好,这三个女生就不会知道其他两个人的存在。
从宏观上来看,(一年)小张同学同时和三个女生交往。渣男
从微观上来看,(一天)小张同学同一时刻只是和一个女生交往。好男人。
换到电脑上操作系统就是这样管理进程的。
只不过现实中CPU运行速度太快,我们感受不到。我们觉得好像CPU是同时在运行这么多进程一样。
进程的优先级:安排时间表的时候优先给谁安排
进程的上下文:将寄存器的信息保存到内存中。记录上次运行到哪个指令,下次再调度的时候就可以很方便的继续从这个位置执行。可以理解为单机游戏的存档,读档。
进程的记账信息:记录这个进程在CPU上执行了多久,用来辅助决定这个进程是否要继续执行还是说要调度出去。
1.2认识线程
为什么需要线程?
我们引入进程的目的,就是为了能够"并发编程"。为了同时运行多个程序,虽然多进程已经能够并发进程了,但是多进程还是有一定的提升空间。
创建进程、销毁进程、调度进程这些操作的开销有点太大了。 为此,引入了线程。
线程
Thread,在有些系统上也叫做"轻量级进程"。为什么说它轻量呢?
创建线程比创建进程更高效;
销毁线程比销毁进程更高效;
调度线程比调度进程更高效;
因为创建线程并没有申请资源,销毁线程也不需要释放资源。直接让线程产生在进程内部,公用之前的资源。
线程和进制是包含的关系。一个进程可以包含多个线程或者一个线程。当创建进程之后,就相当于把资源都分配好了,接着在这个进程里面创建线程,这样的线程就和之前的进程公用一样的资源了。
1.3进程、线程之前的区别和联系(面试题)
1、进程是操作系统资源分配的基本单位,线程是操作系统调度执行的基本单位。
2、进程是包含线程的,一个进程可以含有多个线程,也可以含有一个线程。
3、每个进程都有独立的内存空间(虚拟地址空间),同一个进程的多个线程之间,公用这个虚拟地址空间。
创建线程的几种方式
1、创建自定义类继承Thread类重写run方法
/** * Thread是Java标准库中的一个关于线程的类 * 常用的方式是自定义一个类继承Thread类,然后重写run方法 * 这里的run方法就是线程具体要执行的任务(代码) */ public class threadDemo1 { public static void main (String[] args) { Thread t=new Thread (); //start方法就会在操作系统中创建一个线程出来。 t.start (); } } class MyThread extends Thread{ @Override public void run(){ System.out.println ("继承Thread类创建线程"); } }
2、实现Runable接口,重写run方法
public class threadDemo2 { public static void main (String[] args) { Thread T=new Thread (new myRunable ()); T.start (); } } class myRunable implements Runnable{ @Override public void run(){ System.out.println ("实现Runbale接口,重写run"); } }
3、继承Thread类重写run方法,使用匿名内部类的方式
public static void main (String[] args) { Thread t=new Thread (){ @Override public void run(){ System.out.println ("匿名内部类"); } }; t.start (); }
4、实现Runable,重写run方法,使用匿名内部类
public static void main (String[] args) { Thread t=new Thread (new Runnable () { @Override public void run () { System.out.println ("实现Runable,重写run,使用匿名内部类"); } }); t.start (); }
5、使用lambda表达式
// Thread t3 = new Thread(() -> System.out.println("使用匿名类创建 Thread 子类对象")); Thread t4 = new Thread(() -> { System.out.println("使用匿名类创建 Thread 子类对象"); });