前言
这篇文章只是简单介绍一下多线程的基础和定义,有关多线程与进程的关系,多线程的生命周期,线程池的原理和多线程与RunLoop的关系。
1. 线程与进程
现在的iOS开发都是单进程的。下面就是简单介绍一下有关线程与进程之间的关系和定义。
1.1 线程的定义
- 线程是进程的
基本执行单元,一个进程中的所有任务都在线程中执行。 - 进程要想执行任务,必须得有线程,进程至少要有一条线程。
- 程序启动会默认开启一条线程,这条线程被称为
主线程或者UI线程。
1.2 进程的定义
- 进程是指在系统中正在运行的一个应用程序。
- 每个进程之间是独立的,每个进程均运行在其专用的且受保护的内存中。
1.3 线程与进程的关系和区别
- 地址空间:同一个进程的线程共享本进程的地址空间,而进程之间是独立的地址空间。
- 资源拥有:同一进程内的线程共享本进程的资源如
内存,I/O,cpu等,但是进程之间的资源是独立的。一个进程奔溃后,在保护模式下不会对其他进程产生影响,但是一个线程奔溃整个进程都死掉。所以多进程要比多线程健壮。 - 执行过程:每个独立的进程有一个程序入口和顺序执行序列。但是线程不能独立执行,必须存在应用程序中,由应用程序提供多个线程执行控制。
- 线程是处理器调度的基本单位,进程不是。
1.4 线程与队列的关系
一般来说,线程与队列之间是没有关系的,可以这样理解:
- 队列负责调度任务,线程负责执行任务。
- 在超市
(进程),有3个收银窗口(线程),而只有2条队伍(队列)。 - 收银窗口
(线程)只负责为排队的人收钱买单,并不会管队伍(队列)是怎么排的。
1.5 线程与RunLoop的关系
- RunLoop与线程是一一对应的,一个RunLoop对应一个核心的线程。为什么说是核心呢?因为RunLoop是可以嵌套的,但是核心的只能有一个,它们的关系保存在一个全局的字典里。
- RunLoop是来管理线程的,当线程的RunLoop被开启后,线程会在执行完任务后进入休眠状态,有了任务就会被唤醒去执行任务。
- RunLoop在第一次获取时被创建,在线程结束时被销毁。
- 对于主线来说,RunLoop在程序一启动就默认创建好了。
- 对于子线程来说,RunLoop是懒加载的,只有当我们使用的时候才会被创建,所以在子线程用定时器要注意:确保子线程的RunLoop被创建,不然定时器不会回调。
2. 多线程
- 原理:CPU在单位时间片里快速在各个线程之间切换。
- 优点:
- 能适当提高程序的执行效率。
- 能适当提高资源的利用率(CPU,内存)
- 线程上的任务执行完成后,线程会自动销毁
- 缺点:
- 开启线程需要占用一定的内存空间(默认情况下,每一个线程都占512KB)
- 如果开启大量的线程,会占用大量的内存空间,降低程序的性能
- 线程越多,CPU在调用线程上的开销就越大
- 程序设计更加复杂,比如线程间的通讯、多线程的数据共享等。
2.1 多线程的生命周期
多线程的生命周期的流程:
新建(New):创建和实例化线程就绪(Runnable):调用start方法(注意:此时调用start方法并不是说就开始执行了,还需要等资源,等待cpu的调度)。运行(Running):CPU负责调度可调度线程池中线程的执行,线程执行完成之前,状态可能会在就绪和运行之间来回切换。就绪和运行之间的状态变化由CPU负责,程序员不能干预。阻塞(Blocked):比如调用sleep或者等待锁阻塞线程执行,从可调度线程池移出。当sleep时间到了,此时重新添加到可调度线程池。死亡(Dead):任务执行完毕强制退出。
2.2 线程池
由图可以知道,先判断线程池大小是否小于核心线程池大小
- 如果没有,那就说明此时的线程池容量满了,那么线程池就要判断工作队列是否已经满了。如果满了,线程池就要判断线程是否都在工作,如果都在工作,就交给饱和策略,如果不是都在工作就创建线程去执行任务。如果线程池的工作队列没有满,就提交任务到工作队列去创建线程执行任务。
- 如果有,直接创建线程去执行任务。
2.3 饱和策略
AbortPolicy:直接抛出RejectedExecutionExecption异常来阻止系统正常运行。CallerRunsPolicy:将任务回退到调用者。DisOldestPolicy:丢掉等待最久的任务。DisCardPolicy:直接丢弃任务。 这四种策略均实现RejectedExecutionHandler接口。
3 最后
多线程的基础概念性的内容暂时介绍到这里了。