前言
上一篇了解到进程是可以很好的解决并发编程这样的问题,但在有些场景需要频繁的创建和销毁,此时使用并发编程会让系统开销增大
注:基于多进程的编程模式:服务器同一时刻会收到很多请求,针对每个请求,都会创建一个进程,给这个请求提供一定服务,返回对应的响应;一旦请求处理完进程就要销毁;
若请求过多,服务器需要不停创建新的进程,销毁旧的进程;
频繁创建和销毁,开销增大:资源的申请和释放,进程是资源(CPU,硬盘,内存,网络带宽)分配的基本单位;进程刚启动,内存资源进程需要把依赖的代码和数据从磁盘加载到内存
引入线程可以解决上述问题,线程--"轻量级进程",在进程的基础上做出了改进,保持了独立调度执行--"并发支持",同时省去"分配资源""释放资源"带来的额外开销
线程工作原理
一.PCB描述线程
一个进程可以有多个PCB,表示这个线程包含了一个线程组包含了多个线程(线程组也是进程的一部分)
操作系统进行"多任务调度"本质上是在调度PCB(线程在系统中的调度规则就和之前的进程是一样的,线程的PCB中也有状态,优先级,上下文,记账信息...)
每个进程,都可以包含一个线程/多个线程
线程要解决的问题:要能够降低频繁释放带来的开销
二.进程和线程的关系
有线程之前进程需要扮演两个角色(资源分配的基本单位,也是调度执行的基本单位)
有线程之后,进程专注资源分配(创建进程,资源分配,一个进程至少包含一个线程-创建第一个线程的同时,进程就出来了),线程负责调度执行;
加快效率:1.多进程方案(创建新的进程就需要申请更多的资源)2.使用多线程(资源开销小)
当引入的线程到达一定数量后在尝试继续引入新的线程无法提升,当线程数量太多的时候,线程之间就会相互竞争CPU的资源(CPU核心数有限)非但不会提高效率,反而增加调度开销
总结
1.线程也不是越多越好,要能够合适,如果线程太多了,调度开销就可能非常明显
2.多线程的线程之间,可能会打架,当线程之间起了冲突,会导致代码中出现一些逻辑错误(线程安全问题)
3.资源共享,若一个线程抛出异常,并且没有处理好,可能导致整个线程被终止
4.进程是包含线程的,线程是轻量级进程
5.每个线程,也是一个独立的执行流,可以执行一些代码,并且单独的参与到CPU调度中(状态,上下文,优先级,记账信息,每个线程都有自己的一份)
6.每个进程,有自己的资源,进程中的线程共用这一份资源(内存空间和文件描述符表)
即进程是资源分配的基本单位,线程是调度执行的基本单位
三.多线程编程
Thread类
在java中不推荐多进程进行并发编程,很多和多进程编程相关的api,在java标准库中都没有提供;
多线程并发编程系统提供了编程的api,java标准库把这些api封装了在代码中就可以使用
java提供的api,Thread这样的类:
1.Thread这个类可以直接使用,不需要导包-java-java标准库中有一个特殊的包(java.lang)
2.一个.java文件中,只能有一个public的类,这个类如果没有public包作用就只能在当前包里被其他类使用
一个进程中,至少会有一个线程,这个进程中的第一个线程也就称为"主线程"
此处的run不需要手动调用,会在线程创建好之后,被jvm自动调用执行(类似于main方法-主线程的入口方法)-是一个java进程(程序)的入口方法-回调函数
回调函数是编程中非常重要的概念
1.c-指针进阶=>函数指针
(1)实现转移表,降低圈复杂度(写个计算器)
(2)作为回调函数,(自己实现qsort)
2.java数据结构
优先级队列PriorityQueue-指定比较规则
Comparable(自己和别人比) 只有一种比较规则
Comparator(别人拿自己和别人比) 可以有多重规则
方法重写-override
本质:是让能够对现有的类进行扩展
一个线程执行代码,Thread类本身会带有run入口方法,但标准库自带的run不知道需求必须手动指定,就可以针对原有Thread进行扩展(把一些能够复用的进行重用,需要扩展的进行扩展)
Thread会有很多属性方法,大部分内容都复用即可,只是把需要扩展的这个进行扩展即可.
不写注释可以完成方法重写-是为了方便编译器,对代码进行自动检查(final,throws)
总代码图
运行结果图
此处没有手动调用run,但是run还是执行了,当引入多线程之后,代码中就可以同时具备多个执行流!!!一些逻辑
run是线程的入口,每个线程跑起来都会执行一些逻辑
四.操作系统
定义
最核心的功能模块(管理,管理硬件,给软件提供稳定的运行环境)
操作系统=内核空间(内核态)+用户空间(用户态)
内核空间:提供调用api,在内核中完成应用程序的操作;
用户空间:普通的应用程序
划分用户态和内核态:为了防止应用程序把硬件设备或者软件资源搞坏,系统封装api,这些api都是"合法"的操作,应用程序只能调用api,不至于对系统/硬件设备产生太大危害,若应用程序直接操作硬件极端情况代码出现bug会损害硬件
调度执行
每个线程都是一个独立的执行流,每个线程都能够独立的去CPU上调度执行
当有多线程的时候,线程的执行先后顺序是不确定的:操作系统内核中有一个调度器模块--实现方式类似随机调度的效果
随机调度(抢占式执行):线程被调度到CPU上执行时机不确定;线程从CPU上下来给别人让位时机不确定
通过第三方工具直观了解线程情况:jdk中jconsole工具-分析java进程
其中JVM自带的线程要完成一些垃圾回收(gc自动释放内存),监控统计各种指标,把指标通过网络方式传送给其他程序