你必须知道的几个概念(读书笔记)
- 同步和异步
同步和异步是用来形容一次方法调用,同步方法调用一旦开始,调用者必须等到方法调用返回后,才能继续后续的行为,异步方法调用更像一个消息传递,一旦开始,方法调用就会立即返回,调用者就可以继续后续操作.而异步方法通常会在另外一个线程中真实的执行,整个过程不会阻碍调用者的工作.
打个比方 就想去超市买东西和网上购物一样.
- 并发和并行
他们都可以表示两个或者多个任务一起执行.打会偏重点有些不同,并发偏重于多任务交替执行,而多个任务之间有可能还是串行的.而并行是真正意义上的同时执行.
严格意义上来说,并行的多个任务是真是的同时执行,而对于并发来说,这个过程只是交替的,一会任务a 一会任务b.系统会不停的在两者之间切换,
- 临界区
临界区用来表示一种公共资源或者说是共享数据.可以被多个线程使用,但是每一次,只能有一个线程使用它,一点临界区被占用,其他线程想要使用这个资源,就必须等待.
- 阻塞和非阻塞
阻塞和非阻塞通常用来形容多线程之间的互相影响.比如一个线程占用了临界区资源,那么其他所需要这个资源的线程就必须在这个临界区进行等待,等待会导致线程挂起.这种情况叫阻塞.如果占用资源线程一直不愿释放资源,那么其他所有阻塞在这个临界区上的线程都不能工作!
非阻塞的意思与之相反.它强调没有一个线程可以妨碍其他线程执行.
- 死锁.饥饿 活锁
死锁 饥饿 活锁属于多个线程的活跃性问题. 死锁应该是最糟糕的一种情况了.相当于abcd 相互占用了其他车辆的车道,.大家都不愿意释放自己的资源.
饥饿 是指某一个或者多个线程因为种种原因无法获得所需要的资源,导致一直无法执行. 比如他的优先级可能太低.
活锁是一种有趣的情况,.就是相互礼让.然后碰在一起,在相互礼让.线程没有人智能,相互对视一眼就可以解开,那么就会出现资源不断在两个线程中跳动,而没有一个线程可以同时拿到所有资源而正常执行,
- 并发级别
由于临界区的存在,.多线程之间的并发必须收到控制,根据控制并发策略,我可以把并发的级别进行分类,大致上可以分为阻塞 无饥饿 无障碍 无等待几种
- 阻塞
一个线程时阻塞的 那么在其他线程释放资源之前,当前线程无法继续执行.当我们使用synchronized关键字,或者重入锁时,我们得到的就是阻塞的线程
- 无饥饿
如果线程之间有优先级的.那么线程调度的时候会倾向于满足高优先级的线程.对就是说,对于同一个资源的分配,是不公平的!如果系统允许高优先级的线程插队.那么可能导致低优先级线程产生饥饿,但如果锁时公平的,满足先来后到.那么饥饿就不会产生,不管新来的线程优先级有多高,想要资源,就要排队.
- 无障碍
无障碍是一种最弱的非阻塞调度,两个线程如果是无阻碍的执行,那么他们就不会因为临界区的问题导致一方挂起,
如果说阻塞的控制方式是悲观策略,系统认为两个线程之间很有可能发生不幸的冲突,因此 保护数据共享数据为第一优先级,
非阻塞的调度就是一种乐观的策略 他认为多线程之间有可能不会发生冲突,或者说这种概率不大,因此大家都应该无障碍的执行,但是一但检测到冲突,就应该回滚,
一种可行的无障碍实现可以依赖一个一致性标记来实现,线程操作之前,先读取并保存这个标记,在操作完成之后,再次读取,检查这个标记是否被更改过,如果两者一致 则说明资源访问没有冲突,如果不一致 责说明资源可能在操作过程中于其他写线程冲突,需要重试操作.
- 无锁
无锁的并行都是无障碍的,在无锁的情况下,所有线程都能尝试对临界区进行访问但不同的是,无锁的并发保证必然有一个线程能够在有限步内完成操作离开临界区,
在无锁的调用中,一个典型的特别是可能包含一个无穷循环,在这个循环中,线程不断地尝试修改共享变量,如果没有冲突,修改成功,那么程序退出,否则继续尝试修改,但无论如何,无锁的并行总能保证有一个线程是可以胜出的,不至于全军覆没,至于临界区中竞争失败的线程,他们则不断重试,知道自己获胜,如果运气很不好,如果尝试不成功,则会出现类似饥饿的现象,线程会停止不前,.
- 无等待
无锁只要求有一个线程可以在有限步内完成操作,而无等待则在无锁的基础上更进一步进行拓展,他要求所有的线程都必须所有限步内完成, 这样就不会引起饥饿问题,如果限制这个步骤上限,还可以进一步分解为有界无等待和线程数无关的无等待几种,他们之间的区别只是对循环次数的限制不同,
一个典型的无等待结果是RCU.他的基本思想是 对数据的读可以不加控制,因此 所有线程都是无等待的,他们既不会被锁定也不会引起任何冲突,但在写数据的时候,先取得原始数据的副本,接着只修改副本数据,修改完成后,在合适的时候回写数据