1. 概念
1.1. 同步和异步
同步和异步通常用来形容一次方法调用.
同步方法调用一旦开始, 调用者必须等到方法调用返回, 才能继续后续的行为.
异步方法调用一旦开始, 方法调用就会立即返回, 调用者就可以继续后续的操作; 而真正的执行是在另外一个线程中, 不会阻碍调用者.
1.2. 阻塞和非阻塞
阻塞和非阻塞通常用来形容多线程间的相互影响.
阻塞表示线程因某种原因导致了其他线程等待或挂起. 比如某个线程占用了临界区, 其他线程同时也想使用这个资源就会被阻塞.
非阻塞表示线程之间互不影响, 不会妨碍其他线程工作.
1.3. 并发和并行
并发和并行用来形容多个任务一起执行.
并发表示多个任务交替执行.
并行表示多个任务同时执行.
1.4. 临界区
临界区用来表示一种公共资源或者共享数据, 可以被线程使用.
但同一资源在同一时间只有被一个线程占用, 而其他线程同时也想使用这个资源, 必须等待资源的被释放.
1.5. 死锁
多个线程并发争夺多个资源时, 因为相互抢占资源, 导致谁都无法继续执行.
1.6. 死锁的四个必要条件
- 互斥
一个资源在同一时间只能被一个线程占有. - 请求与保持
线程已经占有了至少一个资源, 但又提出新的资源请求, 但是该资源已经被其他线程占有, 此时资源请求被阻塞, 但对已占有的资源保持不放. - 不可剥夺
在资源未被主动释放前, 其他线程不能强行剥夺资源. - 循环等待
若干线程形成循环等待资源的关系.
1.6.1. 如何处理死锁
- 预防死锁
通过破坏四个必要条件中是一个或几个来防止死锁的发生. - 检测死锁
允许系统运行时发生死锁, 但检测死锁是否发生, 并干预解除死锁.
1.7. 饥饿
饥饿是指线程因为某些原因导致无法获取到资源, 导致无法继续执行.
1.8. 活锁
多个线程并发争夺多个资源时, 因为相互谦让资源, 导致谁都无法继续执行.
1.9. 并发级别
- 阻塞
synchronized, 锁会导致线程阻塞. - 无饥饿
线程之间非公平竞争, 如线程优先级, 非公平锁等会导致线程饥饿; 相反, 如果线程之间公平竞争, 那么任何线程都有机会执行. - 无障碍
无障碍是最弱的非阻塞调度.
线程之间互不影响的执行, 谁都可以进入临界区修改数据, 但是并发情况下不能保证数据安全. - 无锁
无锁的并行必定是无障碍的.
在无锁情况下, 所有线程都能尝试对临界区进行访问, 无锁并发能保证必定有一个线程能够在有限步骤内完成操作离开临界区. - 无等待
无等待是在无锁的基础上扩展, 要求所有线程都必须在有限步骤内完成操作. 这也同时解决了饥饿问题.
典型的无等待结构就是 RCU (Read-Copy-Update). 它的基本思想就是, 对数据的读不加控制, 因此所有的读线程操作都是无等待的; 写数据时, 先取得原始数据的副本, 接着只修改副本数据, 修改完成后, 在合适的时机会写数据.