原文:http://doc.akka.io/docs/akka/2.3.6/general/terminology.html 译者:吴京润
本章我们试图建立一个通用的术语列表,用来定义有关并发和分布式系统的坚实的基础,而这也是akka的目标。请注意,在这些术语当中许多并没有一致的定义。我们简单的寻求在akka文档范围内使用的工作定义。
并发与并行
并发与并行是相关的概念,但是也有很多细节上的差异。并发意味着两个或更多的任务正在取得进展,即使它们不是同时执行的。例如,可以用时间片的方式实现这一点,每个任务在时间片内执行一小部分,并与其它任务的切片混合执行。并行的出现使任务实现了真正的同时执行。
异步与同步
如果方法的调用者在方法返回或抛出异常前不能有任何进展,这个方法调用就被认为是同步的。然而异步调用允许调用者可以取得有限的进展,调用的方法完成时会通过其它机制(可能是注册的回调、Future对象或一条消息)向调用者发送信号。
一个同步API可能使用阻塞方式实现,但这不是必须的。对于一个CPU密集型任务可能有类似的阻塞行为。一般情况下,异步API是更好的选择,因为它们确保任务可以取得进展。角色是天生异步的,一个角色能够在发送一条消息后不用等待实际的传输行为而继续执行。
非阻塞与阻塞
我们谈论的阻塞是指一个线程的延迟可以无限期延迟其它一些线程。一个不错的例子是一个线程以互斥的方式独占一项资源。如果一个线程无限期的(比如不小心运行了一个无限循环)持有这项资源,而其它线程因等待该资源而无法继续运行。与此相反,非阻塞意味着没有线程可以无限期的延迟其它线程。
非阻塞操作比阻塞操作要优先选择,当系统中包含阻塞操作时,系统整体的进度就不能得到一般性的保证。
死锁、饥饿与活锁
死锁的出现是因为多个参与者同时互相等待对方到达某个特定状态进而得以继续,然而它们都因为其它参与者不能到达这个特定状态(一个“第二十二条军规”问题)而不能继续,所有相关子系统都不能被中断。死锁与阻塞紧密相关,因为一个参与者线程有能够无限期延迟其它线程的必要。
发生了死锁时,没有参数者可以继续执行,而饥饿恰好与之相反,当有参与者可以执行时,可能会有一个或更多得不到执行机会。典型场景是一个简单调度算法问题挑选高优先级任务。如果持续传入足够多的高优先级任务,低优先级任务永远也不会执行。
活锁类似死锁,没有参与者可以继续执行。所不同的是,不是在等待其它参与者的进度,而是参与者自己不断改变自己的状态。一个示例场景是两个参与者有两个相同的可用资源。它们都试图获得资源,但是它们还会检查是否有其它参与者也需要这个资源。当资源被其它参与者请求时,它们尝试得到这个资源的其它实例。在不走运的时候,可能会发生两个竞争者在两个资源之间反弹,永远也不占有它们,一直迁就于其它参与者。
竞态条件
我们说的竞态条件是指有关一组事件的顺序的假设可能会受外部不确定性因素的影响。竞态条件经常发生在多线程拥有一个共享可变状态,而线程交替操作引发的意外行为。虽然这是一种常见情况,共享状态也不必出现竞态条件。一个例子是,客户端向服务器发送无序数据包(UDP数据报就是这样的例子) P1,P2。数据包可能经由不同的网络路径,服务器可能先收到P2再收到P1。如果消息不包含有关它们的发送顺序的信息,服务器就不能确定数据包的顺序了。依赖于数据包的意义就可能引发竞态条件。
注意
AKKA只保证在一对给定角色之间传递的消息始终保持消息传递的顺序。见消息传递的可靠性。
非阻塞担保(进展状况)
在前面的章节中,因为一些原因而不推荐阻塞,包括死锁的危险,降低系统吞吐量。接下来的部分我们从不同程度上讨论多种非阻塞属性。
无等待
一个无等待的方法是指每次调用确保在有限步骤内结束。如果一个方法是有界无等待的,它的步数就有一个上限。
基于这个定义,无等待方法永远不阻塞,因此也不会死锁,因为每一部分都可以在有限步数以后继续(当调用结束时),无等待方法也不会发生饥饿。
无锁定
无锁定属性比无等待要弱。发生无锁定的调用时,方法常常在有限步骤内完成。这一定义意味着无锁定的调用不会发生死锁。另一方面,确保一些方法在有限步内完成不足以确保所有调用最终都会完成。换句话说,元锁定不足以确保不会发生饥饿。
无阻塞
无阻塞是本文讨论的比无锁定还要弱的保证。一个无阻塞的方法调用是指如果有一个隔离执行的时间点(其它线程不执行任何步骤,比如挂起),它在有限步数内结束。所有无锁定的对象都是无阻塞的,但是反过来通常不成立。
乐观并发控制(OCC)方法通常是无阻塞的。OCC的做法是,每一个参与者试图操作共享对象,但是如果一个参与者探测到与其它参与者冲突,它就回滚所有个性,并按照某些调度策略再次尝试。如果某个时间点上有且只有一个参与者在尝试,操作就会成功。