多线程基础
1、线程
1.1定义
一个基本的CPU执行单元 & 程序执行流的最小单元,比进程更小的可独立运行的基本单位,可理解为:轻量级进程
组成:线程ID + 程序计数器 + 寄存器集合 + 堆栈
注:线程自己不拥有系统资源,与其他线程共享进程所拥有的全部资源。
1.2作用
减少程序在并发执行时所付出的时空开销,提高操作系统的并发性能。
1.3 状态及状态转换
1.4分类
1.41 守护线程
守护用户线程的线程,即在程序运行时为其他线程提供一种通用服务,如垃圾回收线程
1.42 非守护线程
a.主线程(UI线程)
Android系统在程序启动时会自动启动一条主线程,用于处理四大组件与用户进行交互的事情(如UI、界面交互相关)
注:因为用户随时会与界面发生交互,因此主线程任何时候都必须保持很高的响应速度,所以主线程不允许进行耗时操作,否则会出现ANR
b.子线程(工作线程)
用户手动创建的线程,用于处理耗时的操作(网络请求、I/O操作等)
c.守护线程 & 非守护线程的区别
区别:虚拟机是否已退出:
当所有用户线程结束时,因为没有守护的必要,所以守护线程也会终止,虚拟机也同样退出;反过来,只要任何用户线程还在运行,守护线程就不会终止,虚拟机就不会退出。守护线程不属于不可或缺的存在。
1.5 优先级
线程优先级分为10个级别,分别用Thread类常量表示。
Thread.MIN_PRIORITY // 优先级1
Thread.MAX_PRIORITY // 优先级10
通过方法 setPriority(int grade) 进行优先级设置
默认线程优先级是5,即 Thread.NORM_PRIORITY,数字越高,优先级越高
2、进程
2.1 定义
是进程实体的运行过程 & 系统进行资源分配和调度的一个独立单位
2.2 状态
线程与进程区别
3、多线程
3.1 定义
一个程序(进程)运行时产生多个线程(任务)同时进行
3.2 多线程目的
提高CPU资源的利用率,包括以下三点:
- 避免阻塞(异步调用)
单个线程中的程序,是顺序执行的。如果前面的操作发生了阻塞,那么就会影响到后面的操作。 - 避免CPU空转
如果服务器只用单线程,即处理完一条HTTP请求,再处理下一条请求的话,CPU会存在大量的闲置时间。因为处理一条请求,经常涉及到RPC、数据库访问、磁盘IO等操作,这些操作的速度比CPU慢很多,而在等待这些响应的时候,CPU却不能去处理新的请求,其余的请求只能一直处于等待状态,因此服务器的性能就很差 - 提升性能
多线程可以同时执行多个任务,对于可并发执行的任务而言,确实提高了性能。
3.3 并行与并发
并行:多个cpu实例或者多台机器同时执行一段处理逻辑,是真正的同时。
并发:单位时间内运行的线程数,离开时间量度就没有意义。通过cpu调度算法,让用户看上去同时执行,实际上从cpu操作层面不是真正的同时。其实,多线程本质是并发执行程序。因为计算机任何特定时刻只能执行一个任务;多线程只是一种错觉:只是因为JVM快速调度资源来轮换线程,使得线程不断轮流执行,所以看起来好像在同时执行多个任务而已(异步执行)
3.4 同步与异步
同步:发送一个请求,等待返回,然后再发送下一个请求;同步可以避免出现死锁,读脏数据的发生,可以保证安全性。
异步:发送一个请求,不等待返回,随时可以再发送下一个请求;异步则是可以提高效率,可以并发执行多项工作。
3.5 线程安全
一段代码是线程安全的是指:在并发的情况之下,该代码经过多线程使用,线程的调度顺序不影响任何结果。这个时候使用多线程,我们只需要关注系统的内存,cpu是不是够用即可。
线程不安全就意味着线程的调度顺序会影响最终结果,则程序执行的结果并不是我们想要的结果,甚至有可能会导致实际场景严重的安全问题。
3.6 多线程开发原则
1、不要阻塞UI线程(即主线程):单线程会导致主线程阻塞,然后出现ANR错误:主线程被阻塞超过5s则会出现错误,(将耗时任务放在工作线程中执行)
2、不要在UI线程之外更新UI组件 (将更新UI组件放在主线程中执行)
3.7 线程调度
3.7.1调度方式
1、当系统存在大量线程时,系统会通过时间片轮转的方式调度线程,因此线程不可能做到绝对的并发
2、处于就绪状态(Runnable)的线程都会进入到线程队列中等待CPU资源(同一时刻在线程队列中可能有很多个)
3、在采用时间片的系统中,每个线程都有机会获得CPU的资源以便进行自身的线程操作;当线程使用CPU资源的时间到后,即时线程没有完成自己的全部操作,JVM也会中断当前线程的执行,把CPU资源的使用权切换给下一个队列中等待的线程。(被中断的线程将等待CPU资源的下一次轮回,然后从中断处继续执行)
3.7.2调度优先级
Java虚拟机(JVM)中的线程调度器负责管理线程,并根据以下规则进行调度:
1、根据线程优先级(高-低),将CPU资源分配给各线程
2、具备相同优先级的线程以轮流的方式获取CPU资源
3.7.3 产生死锁条件
- 互斥:每个资源要么已经分配给了一个进程,要么就是可用的。
- 占有和等待:已经得到了某个资源的进程可以再请求新的资源。
- 不可抢占:已经分配给一个进程的资源不能强制性地被抢占,它只能被占有它的进程显式地释放。
- 环路等待:有两个或者两个以上的进程组成一条环路,该环路中的每个进程都在等待下一个进程所占有的资源。
3.7.4死锁的处理方法
- 鸵鸟策略
当发生死锁时不会对用户造成多大影响,或发生死锁的概率很低,可以采用鸵鸟策略(忽略它) - 死锁的检测与死锁恢复
检测:从一个节点出发进行深度优先搜索,对访问过的节点进行标记,如果访问了已经标记的节点,就表示有向图存在环,也就是检测到死锁的发生 - 死锁的预防
(1)破坏互斥条件
例如假脱机打印机技术允许若干个进程同时输出,唯一真正请求物理打印机的进程是打印机守护进程。
(2)破坏占有和等待条件
一种实现方式是规定所有进程在开始执行前请求所需要的全部资源。
(3)破坏不可抢占条件
(4)破坏环路等待
给资源统一编号,进程只能按编号顺序来请求资源。 - 死锁的避免
算法要做的是判断对请求的满足是否会进入不安全状态,如果是,就拒绝请求;否则予以分配