通过上个博客给大家主要介绍了进程和进程管理一些概念,这里我们简单回顾一下
进程是什么?跑起来的程序(.exe可执行文件)就是进程,通过双击.exe可执行文件就在任务管理器看到一个进程在跑了,这个进程是怎么出现的呢?操作系统会创建一个pcb,把这个pcb加入到链表,后续通过遍历链表就可以获取到每一个进程了,
进程调度,为啥要进程调度,因为狼多肉少,进程非常多,但是cpu核心非常少,所以cpu核心通过并发执行进程来完成,只要切换的速度足够快,在咱们宏观视角里就是一起并发执行的,而这里pcb提供一些属性来支持进程调度或者并发的效果,进程的状态,进程的优先级,进程的上下文和进程的记账信息,操作系统引入进程的目的就是为了进行多任务并发执行的效果,以前有的操作系统就是单任务的,你想启动新的任务必须退出旧的任务
线程
进程包含线程,一个进程里有一个线程或多个线程,每个线程都是一个独立的执行流,换言之就是多个线程之间是并发执行的,这里的并发是并发+并行,而这里需要说明一点操作系统真正调度的是线程,而之前所说的是进程里面只有一个线程(本质上调度线程和调度进程是一样的效果),但是我们现在考虑多个线程此时我们调度的是以线程为单位进行调度,线程是操作系统调度运行的基本单位
一个进程之前的线程共用同一份系统资源
只有在进程启动,创建第一个线程需要申请分配资源,此时后面的线程不必再申请资源,
线程也不是越多越好,cpu核心数是固定的,此时进一步提升线程的数量,会导致不能并发执行从而降低效率(调度开销,线程太多导致调度次数增加反而增加调度开销),总并发程度是固定的,不会因为线程多提升干活效率,真正解决方案是再加cpu
为了更好解释线程和进程的区别和一些优缺点我们举一个例子
此时有一个滑稽老铁在桌子上吃100只鸡肉
但是这样吃下去吃的很慢效率很低下,所以我们启用另一个办法再加一个房子和一张桌子,采用多进程吃鸡方式
此时一人50只鸡肉,大大提升了吃鸡效率,但是这种方式开销比较大需要多搞一个房子和桌子,而且进程之间具有隔离性彼此看不到对方的进度,所以我们采用多线程
这样做的好处一是大大节约了成本,两个线程共用同一份内存资源(房子桌子),而且彼此都能看见对方,大大提升效率,接下来继续提升滑稽老铁的数量,效率进一步提升,但是线程也不是越多越好,此时桌子围满了,边缘的滑稽老铁抢不上来只能阻塞等待被调度,此刻边缘的滑稽老铁不能并发执行并没有提升吃鸡效率,而且就算被调度也是挤下去一个再调度上来一个,总并发程度并没有改变,再开一个进程也没用,这个进程也是在同一个cpu上运行,cpu核心数并没有变多,真正解决办法是再搞一个cpu(主机),在线程调度过程中,如果有一个线程出先异常,会把整个进程都带走还有线程.
.
进程和线程的区别
1.进程包含线程
2.进程有自己的内存资源和文件描述符表,而多个线程之间是共用同一份内存资源和文件描述符表
3.进程是操作系统分配资源的基本单位,线程是操作系统调度的基本单位
4.多个进程之间具有独立性(隔离性),一个进程出现问题不会影响另一个进程,而多个线程之间是没有隔离性,一个线程崩溃,会带走整个进程和线程.
java这里提供Thread类来进行多线程编程
这里我们创建Mythread类来继承Thread类重写run方法,run方法体就是我们创建线程要执行的代码,t指的是子类的对象,通过t调用start方法创建一个线程来调用run方法.上面代码涉及到两个线程一个是main(主线程),另一个是我们通过t.start创建的线程,为了让大家都够理解每个线程都是单独的执行流这里我改下代码
运行结果
main线程执行到t.start后,兵分两路主线程继续往下走,t线程执行run方法,两个线程并发执行,通过运行结果这两个线程就是在同时执行,但是这里谁先第一个打印自己的逻辑代码是不确定的,因为进程是随机调度的(其实操作系统真正的是在调度线程,之前说的调度进程指的是进程里面只有一个线程,调度进程就和调度线程就是一样的效果),切记这里不能直接通过实例调用run方法,
此时并没有创建线程,main线程调用进入run方法后执行死循环, 底下的main正在运行就不会执行到,这里run方法是我们通过t.start创建线程,线程会去调用的,不用我们自己调用。
为了让循环执行慢点可以加上Thread.sleep方法更直观看见两个线程执行的代码
参数单位是ms,1000ms就是1秒,它会睡眠一秒再往下执行,以上是通过子类继承Thread重写run方法创建线程,下面我们介绍另外几种
2.实现Runnable重写run
Runnable字面意思是可运行的,用它来描述具体一个任务
3.继承Thread使用匿名内部类
4.实现Runnable使用匿名内部类
5.lambda表达式