翻译自:http://doc.akka.io/docs/akka/2.3.8/general/terminology.html
术语、概念
在本章,我们尝试建立一个通用的术语来定义一个坚固的基础,用于沟通 Akka 致力于的关于并发、分布式系统。请注意,对于很多这些术语,没有单一的认同的定义。我们仅仅试图给出在这篇 Akka 文档范围内使用的可行定义。
并发 vs. 并行
并发和并行是相关的概念,但他们有细微的不同。并发意味着两个或多个任务在取得进展,即使它们可能不是同时执行。例如通过时间分片来实现,任务的不同部分顺序地执行,混杂着其他任务的部分。并行在另一方面是 在真正地同时执行时出现。
异步 vs. 同步
如果调用者在方法返回一个值或抛出异常前不能取得进展,则个方法调用被认为是同步的。在另一方面,一个异步调用允许调用者取得进展,在一个有限数量的步骤后,并且方法完成后会收到通知,通过额外的机制(可以是注册一个回调、一个 Future 或 一个消息)。
一个同步的 API 可能使用阻塞来实现同步,但不是必须的。一个 CPU 敏感的任务可能表现出类似阻塞的行为。一般情况下,倾向于使用异步的 API,因为它们保证系统可以取得进展。Actors 天生是异步的:一个 actor 在发出消息后可以继续取得进展,不需要等待实际的发送(delivery)发生。
非阻塞 vs. 阻塞
如果一个线程能够无限延迟其他一些线程,我们认为是阻塞的。一个好的例子是,资源可以被一个线程通过互斥独占地使用。如果某个线程无限地(例如意外地跑入一个死循环)持有资源,其他在等待资源的线程将不能取得进展。与此相反,非阻塞意味着没有线程能够无限地延迟其他线程。
非阻塞操作优于阻塞的,因为,当包含阻塞操作时系统的整体进展没法保证。
死锁 vs. 饥饿 vs. 活锁
死锁出现在多个参与者为了到达某个特定的状态以便取得进展而互相等待时。它们中没有一个能够获得进展,如果其他参与者不能到达一个特定的状态,所有受影响系统都将僵失速(stall)。死锁与阻塞是紧密相关的,因为必须存在某个参与者线程能够无限延迟其他线程的进展。
在死锁的情况下,没有参与者能够取得进展,对比地,饥饿发生时,存在一些参与者能够取得进展,但也可能有一个或多个不能。典型的场景是,幼稚的调度算法总是选择高优先级的任务而不是低优先级的。如果进来的高优先级任务的数量总是够多,低优先级的没法完成。
活锁类似于死锁,没有参与者可以取得进展。区别是,不是被冻结在等待其他参与者取得进展的状态里,而是参与者不断地改变他们的状态。一个示例场景是,两个参与者,两个完全相同的资源可得。它们都尝试去获得资源,但它们也会检查另一个是否也需要资源。如果某个资源被另一个参与者请求了,它们尝试去获得另一个资源的实例。在不幸的情况下可能出现两个参与者在两个资源之间弹跳(bounce):从不占有它,但总是退让让对方。
竞争条件
当对一组事件的次序关系的假设被外部的不确定的因素违背时,我们称为竞争条件。竞争条件经常出现在多线程有一个共享的可变状态,且线程对状态的操作可能交叉进行,导致非预期的行为。一个常见的情况是,共享状态不总是导致竞争条件。一个例子是客户端可能发送无序的包(如 UDP 数据报) P1、P2 给服务器,因为包可能在不同的网络上路由,可能出现服务器先接收到 P2,再接收到 P1。如果消息不包含关于它们的发送顺序,那么服务器就不可能决定它们的发送顺序。依赖于包的含义,这可能导致竞争条件。
注意:Akka 提供的唯一保证是,在给定 actor 对之间发送的消息的顺序是保留的。
非阻塞保证(进展条件)
在前面的章节讨论了阻塞是不可取的,有几个理由,包括死锁的风险和降低系统吞吐量。在下面的章节讨论不同的不同程度的非阻塞属性。
无等待(wait-freedom)
一个方法是无等待的,如果它保证总是在有限数量的步骤内完成。如果一个方法是有界无等待的,那么步骤的数量有一个有限的上限界限。
从这个定义,允许无等待方法从不阻塞,因此死锁不会发生。另外,因为每个参与者能在有限数量的步骤后(当调用结束时)取得进展,无等待方法是无饥饿的。
无锁(Lock-freedom)
无锁是弱于无等待的。在调用无锁的情况下,通常一些方法在有限数量的步骤内完成。这个定义意味着无锁调用没有死锁。在另一方面,这保证了一些调用在有限数量的步骤内完成不足以保证所有的最终都完成了。换句话说,无锁不足以保证无饥饿。
无阻碍(Obstruction-freedom)
无阻碍是这里讨论的最弱的非阻塞保证。一个方法被称为是无阻碍的,如果存在某个点之后,它的执行是独立的(其他线程不能取得进展,例如被挂起suspended),在有限数量的步骤内完成。所有无锁对象都是无阻碍的,反之不一定成立。
乐观并发控制(Optimistic concurrency control, OCC)的方法一般是无阻碍的。OCC 的方法是,每个参与者尝试在共享对象上执行它自己的操作,如果某个参与者检测到来自其他参与者的冲突,它回滚修改,通过某些调度机制重试。如果存在一个时间点,某个参与者是唯一的尝试者,操作将成功。
推荐的文献
- 《 The Art of Multiprocessor Programming 》 M. Herlihy and N Shavit, 2008. ISBN 978-0123705914
- 《 Java Concurrency in Practice 》 B. Goetz, T. Peierls, J. Bloch, J. Bowbeer, D. Holmes and D. Lea, 2006. ISBN 978-0321349606
欢迎关注我的微信公众号: coderbee笔记,可以更及时回复你的讨论。