本文翻译自:What's the difference between deadlock and livelock?
有人可以举例说明(代码) 死锁和活锁有什么区别吗?
参考:https://stackoom.com/question/PpRX/死锁和活锁有什么区别
Livelock 活锁
A thread often acts in response to the action of another thread. 一个线程通常会响应另一个线程的操作而行动。 If the other thread's action is also a response to the action of another thread, then livelock may result. 如果另一个线程的动作也是对另一个线程的动作的响应,则可能会导致活锁。
As with deadlock, livelocked threads are unable to make further progress . 与死锁一样,活锁的线程无法继续前进 。 However, the threads are not blocked — they are simply too busy responding to each other to resume work . 但是, 线程没有被阻塞 -它们只是太忙于彼此响应而无法恢复工作 。 This is comparable to two people attempting to pass each other in a corridor: Alphonse moves to his left to let Gaston pass, while Gaston moves to his right to let Alphonse pass. 这相当于两个人试图在走廊中互相经过:阿方斯(Alphonse)向左移动以让加斯顿(Gaston)通过,而格斯顿(Gaston)向右移动以让Alphonse通过。 Seeing that they are still blocking each other, Alphonse moves to his right, while Gaston moves to his left. 看到他们仍然互相阻挡,阿方斯(Alphonse)向右移动,而加斯顿(Gaston)向左移动。 They're still blocking each other, and so on... 他们仍然互相阻碍,依此类推...
The main difference between livelock and deadlock is that threads are not going to be blocked, instead they will try to respond to each other continuously. 活锁和死锁之间的主要区别在于,线程不会被阻塞,而是会尝试不断地相互响应。
In this image, both circles (threads or processes) will try to give space to the other by moving left and right. 在此图像中,两个圆圈(线程或进程)都将尝试通过左右移动来给另一个空间。 But they can't move any further. 但是他们不能继续前进。
All the content and examples here are from 这里的所有内容和示例均来自
Operating Systems: Internals and Design Principles 操作系统:内部和设计原则
William Stallings 威廉·斯托林斯
8º Edition 8º版
Deadlock : A situation in which two or more processes are unable to proceed because each is waiting for one the others to do something. 死锁 :一种情况,其中两个或多个进程无法进行,因为每个进程都在等待另一个进程在做某事。
For example, consider two processes, P1 and P2, and two resources, R1 and R2. 例如,考虑两个进程P1和P2,以及两个资源R1和R2。 Suppose that each process needs access to both resources to perform part of its function. 假设每个进程都需要访问两个资源才能执行其部分功能。 Then it is possible to have the following situation: the OS assigns R1 to P2, and R2 to P1. 然后可能出现以下情况:OS将R1分配给P2,将R2分配给P1。 Each process is waiting for one of the two resources. 每个进程都在等待两种资源之一。 Neither will release the resource that it already owns until it has acquired the other resource and performed the function requiring both resources. 在获得另一资源并执行需要这两种资源的功能之前,它们都不会释放它已经拥有的资源。 The two processes are deadlocked 这两个过程陷入僵局
Livelock : A situation in which two or more processes continuously change their states in response to changes in the other process(es) without doing any useful work: 活锁 :两个或多个进程在不做任何有用工作的情况下,不断响应其他进程的更改而更改其状态的情况:
Starvation : A situation in which a runnable process is overlooked indefinitely by the scheduler; 饥饿 :调度程序无限期地忽略可运行进程的情况; although it is able to proceed, it is never chosen. 尽管它能够继续进行,但从未选择。
Suppose that three processes (P1, P2, P3) each require periodic access to resource R. Consider the situation in which P1 is in possession of the resource, and both P2 and P3 are delayed, waiting for that resource. 假设三个进程(P1,P2,P3)每个都需要定期访问资源R。请考虑以下情况:P1拥有该资源,并且P2和P3都被延迟,等待该资源。 When P1 exits its critical section, either P2 or P3 should be allowed access to R. Assume that the OS grants access to P3 and that P1 again requires access before P3 completes its critical section. 当P1退出其关键部分时,应该允许P2或P3访问R。假定OS授予对P3的访问权限,并且P1在P3完成其关键部分之前再次需要访问。 If the OS grants access to P1 after P3 has finished, and subsequently alternately grants access to P1 and P3, then P2 may indefinitely be denied access to the resource, even though there is no deadlock situation. 如果OS在P3完成后授予对P1的访问权限,然后又交替授予对P1和P3的访问权限,那么即使没有死锁情况,P2也可能无限期地被拒绝访问资源。
APPENDIX A - TOPICS IN CONCURRENCY 附录A-同步主题
Deadlock Example 死锁示例
If both processes set their flags to true before either has executed the while statement, then each will think that the other has entered its critical section, causing deadlock. 如果两个进程在执行一个while语句之前将它们的标志设置为true,则每个进程将认为另一个进程已进入其临界区,从而导致死锁。
/* PROCESS 0 */
flag[0] = true;
while (flag[1])
/* do nothing */;
/* critical section*/;
flag[0] = false;
/* PROCESS 1 */
flag[1] = true;
while (flag[0])
/* do nothing */;
/* critical section*/;
flag[1] = false;
Livelock Example 活锁示例
/* PROCESS 0 */
flag[0] = true;
while (flag[1]){
flag[0] = false;
/*delay */;
flag[0] = true;
}
/*critical section*/;
flag[0] = false;
/* PROCESS 1 */
flag[1] = true;
while (flag[0]) {
flag[1] = false;
/*delay */;
flag[1] = true;
}
/* critical section*/;
flag[1] = false;
[...] consider the following sequence of events: [...]考虑以下事件顺序:
This sequence could be extended indefinitely, and neither process could enter its critical section. 此序列可以无限期扩展,并且任何过程都不能进入其关键部分。 Strictly speaking, this is not deadlock , because any alteration in the relative speed of the two processes will break this cycle and allow one to enter the critical section. 严格来说,这不是死锁 ,因为这两个过程的相对速度的任何改变都将中断此循环并允许一个进入关键部分。 This condition is referred to as livelock . 这种情况称为活锁 。 Recall that deadlock occurs when a set of processes wishes to enter their critical sections but no process can succeed. 回想一下,当一组进程希望进入其关键部分而没有成功的进程时,就会发生死锁。 With livelock , there are possible sequences of executions that succeed, but it is also possible to describe one or more execution sequences in which no process ever enters its critical section. 使用livelock时 ,可能有成功的执行序列,但是也可能描述一个或多个执行序列,其中没有进程进入其关键部分。
Maybe these two examples illustrate you the difference between a deadlock and a livelock: 也许这两个示例向您说明了死锁和活锁之间的区别:
Java-Example for a deadlock: Java-死锁示例:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class DeadlockSample {
private static final Lock lock1 = new ReentrantLock(true);
private static final Lock lock2 = new ReentrantLock(true);
public static void main(String[] args) {
Thread threadA = new Thread(DeadlockSample::doA,"Thread A");
Thread threadB = new Thread(DeadlockSample::doB,"Thread B");
threadA.start();
threadB.start();
}
public static void doA() {
System.out.println(Thread.currentThread().getName() + " : waits for lock 1");
lock1.lock();
System.out.println(Thread.currentThread().getName() + " : holds lock 1");
try {
System.out.println(Thread.currentThread().getName() + " : waits for lock 2");
lock2.lock();
System.out.println(Thread.currentThread().getName() + " : holds lock 2");
try {
System.out.println(Thread.currentThread().getName() + " : critical section of doA()");
} finally {
lock2.unlock();
System.out.println(Thread.currentThread().getName() + " : does not hold lock 2 any longer");
}
} finally {
lock1.unlock();
System.out.println(Thread.currentThread().getName() + " : does not hold lock 1 any longer");
}
}
public static void doB() {
System.out.println(Thread.currentThread().getName() + " : waits for lock 2");
lock2.lock();
System.out.println(Thread.currentThread().getName() + " : holds lock 2");
try {
System.out.println(Thread.currentThread().getName() + " : waits for lock 1");
lock1.lock();
System.out.println(Thread.currentThread().getName() + " : holds lock 1");
try {
System.out.println(Thread.currentThread().getName() + " : critical section of doB()");
} finally {
lock1.unlock();
System.out.println(Thread.currentThread().getName() + " : does not hold lock 1 any longer");
}
} finally {
lock2.unlock();
System.out.println(Thread.currentThread().getName() + " : does not hold lock 2 any longer");
}
}
}
Sample output: 样本输出:
Thread A : waits for lock 1
Thread B : waits for lock 2
Thread A : holds lock 1
Thread B : holds lock 2
Thread B : waits for lock 1
Thread A : waits for lock 2
Java-Example for a livelock: 活锁的Java示例:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LivelockSample {
private static final Lock lock1 = new ReentrantLock(true);
private static final Lock lock2 = new ReentrantLock(true);
public static void main(String[] args) {
Thread threadA = new Thread(LivelockSample::doA, "Thread A");
Thread threadB = new Thread(LivelockSample::doB, "Thread B");
threadA.start();
threadB.start();
}
public static void doA() {
try {
while (!lock1.tryLock()) {
System.out.println(Thread.currentThread().getName() + " : waits for lock 1");
Thread.sleep(100);
}
System.out.println(Thread.currentThread().getName() + " : holds lock 1");
try {
while (!lock2.tryLock()) {
System.out.println(Thread.currentThread().getName() + " : waits for lock 2");
Thread.sleep(100);
}
System.out.println(Thread.currentThread().getName() + " : holds lock 2");
try {
System.out.println(Thread.currentThread().getName() + " : critical section of doA()");
} finally {
lock2.unlock();
System.out.println(Thread.currentThread().getName() + " : does not hold lock 2 any longer");
}
} finally {
lock1.unlock();
System.out.println(Thread.currentThread().getName() + " : does not hold lock 1 any longer");
}
} catch (InterruptedException e) {
// can be ignored here for this sample
}
}
public static void doB() {
try {
while (!lock2.tryLock()) {
System.out.println(Thread.currentThread().getName() + " : waits for lock 2");
Thread.sleep(100);
}
System.out.println(Thread.currentThread().getName() + " : holds lock 2");
try {
while (!lock1.tryLock()) {
System.out.println(Thread.currentThread().getName() + " : waits for lock 1");
Thread.sleep(100);
}
System.out.println(Thread.currentThread().getName() + " : holds lock 1");
try {
System.out.println(Thread.currentThread().getName() + " : critical section of doB()");
} finally {
lock1.unlock();
System.out.println(Thread.currentThread().getName() + " : does not hold lock 1 any longer");
}
} finally {
lock2.unlock();
System.out.println(Thread.currentThread().getName() + " : does not hold lock 2 any longer");
}
} catch (InterruptedException e) {
// can be ignored here for this sample
}
}
}
Sample output: 样本输出:
Thread B : holds lock 2
Thread A : holds lock 1
Thread A : waits for lock 2
Thread B : waits for lock 1
Thread B : waits for lock 1
Thread A : waits for lock 2
Thread A : waits for lock 2
Thread B : waits for lock 1
Thread B : waits for lock 1
Thread A : waits for lock 2
Thread A : waits for lock 2
Thread B : waits for lock 1
...
Both examples force the threads to aquire the locks in different orders. 这两个示例都强制线程以不同的顺序获取锁。 While the deadlock waits for the other lock, the livelock does not really wait - it desperately tries to acquire the lock without the chance of getting it. 当死锁等待另一个锁时,活锁并没有真正等待-它拼命尝试获取该锁而没有机会获得它。 Every try consumes CPU cycles. 每次尝试都会消耗CPU周期。
Imagine you've thread A and thread B. They are both synchronised
on the same object and inside this block there's a global variable they are both updating; 假设您有线程A和线程B。它们都在同一个对象上synchronised
,并且在此块中有一个全局变量,它们都在更新;
static boolean commonVar = false;
Object lock = new Object;
...
void threadAMethod(){
...
while(commonVar == false){
synchornized(lock){
...
commonVar = true
}
}
}
void threadBMethod(){
...
while(commonVar == true){
synchornized(lock){
...
commonVar = false
}
}
}
So, when thread A enters in the while
loop and holds the lock, it does what it has to do and set the commonVar
to true
. 因此,当线程A进入while
循环并持有锁时,它将执行它必须执行的操作并将commonVar
设置为true
。 Then thread B comes in, enters in the while
loop and since commonVar
is true
now, it is be able to hold the lock. 然后线程B进入,进入while
循环,由于commonVar
现在为true
,因此它能够持有该锁。 It does so, executes the synchronised
block, and sets commonVar
back to false
. 这样做,执行synchronised
块,并将commonVar
设置回false
。 Now, thread A again gets it's new CPU window, it was about to quit the while
loop but thread B has just set it back to false
, so the cycle repeats over again. 现在,再次线程A得到它的新的CPU窗口,它是要退出while
循环,但线程B刚刚设置回false
,如此循环再次重复了。 Threads do something (so they're not blocked in the traditional sense) but for pretty much nothing. 线程可以执行某些操作(因此在传统意义上不会被阻塞),但几乎没有任何作用。
It maybe also nice to mention that livelock does not necessarily have to appear here. 可能还需要提及的是,活锁不一定必须出现在此处。 I'm assuming that the scheduler favours the other thread once the synchronised
block finish executing. 我假设一旦synchronised
块完成执行,调度程序就会偏向另一个线程。 Most of the time, I think it's a hard-to-hit expectation and depends on many things happening under the hood. 在大多数时候,我认为这是一个很难达到的期望,它取决于引擎盖下发生的许多事情。
Taken from http://en.wikipedia.org/wiki/Deadlock : 取自http://en.wikipedia.org/wiki/Deadlock :
In concurrent computing, a deadlock is a state in which each member of a group of actions, is waiting for some other member to release a lock 在并发计算中, 死锁是一种状态,其中一组动作中的每个成员都在等待其他成员释放锁
A livelock is similar to a deadlock, except that the states of the processes involved in the livelock constantly change with regard to one another, none progressing. 活锁类似于死锁,不同之处在于,活锁中涉及的进程的状态彼此之间不断变化,没有进展。 Livelock is a special case of resource starvation; Livelock是资源匮乏的特例; the general definition only states that a specific process is not progressing. 一般定义仅指出特定过程没有进展。
A real-world example of livelock occurs when two people meet in a narrow corridor, and each tries to be polite by moving aside to let the other pass, but they end up swaying from side to side without making any progress because they both repeatedly move the same way at the same time. 当两个人在狭窄的走廊里相遇时,发生了现实生活中的活锁例子,每个人都试图通过移动到一边让对方经过而礼貌,但最终却没有任何进展就左右摇摆,因为他们都反复移动在同一时间相同的方式。
Livelock is a risk with some algorithms that detect and recover from deadlock. 对于某些检测死锁并从死锁中恢复的算法,活锁是一种风险。 If more than one process takes action, the deadlock detection algorithm can be repeatedly triggered. 如果有多个进程采取措施,则死锁检测算法可以重复触发。 This can be avoided by ensuring that only one process (chosen randomly or by priority) takes action. 通过确保只有一个过程(随机选择或优先选择)可以采取措施,可以避免这种情况。