并发编程理论基础一——概念简述
1、线程,进程,协程
- 进程
一个运行起来的程序或软件;
操作系统分配资源的最小单位;
同一时刻运行的进程数不超过CPU核心数;
进程拥有自己的地址空间,启动进程时需要向操作系统申请系统资源
- 线程
cpu调度和执行的基本单位;每个线程拥有独立的运行栈和程序计数器
线程和进程的联系:
进程拥有自己的资源空间,一个进程包含若干个线程,线程与CPU资源分配无关,多个线程共享同一进程内的资源(内存地址空间和内存单元)
进程的调度和切换比线程慢,资源开销大
进程之间不共享全局变量,同一进程下的多个线程之间共享全局变量
- 协程
coroutine,java中貌似没这个概念,go中有,不懂,仅做下对比
一种用户态的轻量级线程;
协程切换时无需线程上下文切换的开销;
无需原子操作锁定及同步的开销;
方便切换控制流,简化编程模型;
2、并发和并行
一个任务可能是进程,也可能是线程,这里统称为任务
- 并行:针对多核CPU而言,多个核心同时执行多个不同任务的能力
- 并发:针对单核CPU而言,指的是CPU通过时间分片技术交替执行不同任务的能力
多核CPU中,并发和并行一般都会同时存在
3、阻塞和非阻塞
关注的是程序在等待调用结果(消息,返回值)时的状态.
- 阻塞:指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回
- 非阻塞:指在不能立刻得到调用结果之前,该调用不会阻塞当前线程
4、同步和异步
- 同步:调用结果返回之前,进程一直等待,直到拿到返回结果才继续后续操作
- 异步:不导致请求进程阻塞的设计就是异步的
5、为什么要使用多线程
简单说就是为了充分发挥CPU的优势,充分利用CPU资源,为了性能
6、线程安全
引用《并发编程实践》中的一个定义描述:当多个线程访问某个类时,这个类始终都能表现出正确的行为,
就称这个类是是线程安全的
7、上下文切换
CPU通过时间分片来执行任务,当前任务执行一个时间片后会切换到下一个任务。但是,在切换前会
保存上一个任务的状态,以便下次切换回这个任务时,可以再加载这个任务的状态。
任务从保存到加载的过程就是一次上下文切换。上下文切换会影响多线程的执行速度
8、多线程关注哪些问题
(1)安全性问题
(2)活跃性问题
- 死锁和活锁:死锁时两个线程都处于阻塞状态,都想获得对方手上的某个锁,但是都不释放;
活锁不会阻塞线程,但是两个线程一直tryLock,但是都过于主动让出资源,反而没有人真正使用临界区资源
维基百科中的一个形象例子:两个人在路口相遇碰上了,都没有等对方让路,而是都很有礼貌的给对方让路,
- 饥饿:线程由于某种原因迟迟无法访问它所需要的资源
(3)性能问题
9、发布和逸出
- 发布:发布一个对象,就是使对象能够在当前作用域之外的代码中使用
- 逸出:当某个不该发布的对象被发布时就被称为逸出
10、不可变对象
某个对象在被创建后,其状态就不能被修改,这个对象称为不可变对象,不可变对象一定是线程安全的,如String
还有一个事实不可变对象,指的是对象从技术上来看是可变的,但是其状态在发布后不会再改变,这个对象就称为事实不可变对象
11、线程死锁
两个或多个线程互相持有对方所需要的资源,导致这些线程处于相互等待状态,若无外力作用,他们将无法继续执行下去
原因概括:当前线程拥有其他线程需要的共享资源;当前线程等待其他线程释放自己需要的资源;都不放弃
四个必要条件:互斥,占有且等待,不可抢占,循环等待
如何预防:尽量减少嵌套同步,尽量减少锁的定义,一次性申请好锁,申请不到锁时主动释放,如JUC Lock里的tryLock(synchronized就是不释放的典型),获取锁时避免顺序交叉