死锁和活锁有什么区别?

时间:2011-05-27 17:52:40

标签: multithreading pthreads deadlock livelock

有人可以解释(代码)示例死锁活锁之间有什么区别?

7 个答案:

答案 0 :(得分:350)

取自http://en.wikipedia.org/wiki/Deadlock

  

在并发计算中,死锁是一组操作中的每个成员正在等待某个其他成员释放锁的状态

     

livelock 类似于死锁,   除了状态   活锁涉及的过程   不断改变一个   另一个,没有进展。活锁是   资源匮乏的特例;   一般定义只说明   特定的过程不是   进展。

     

一个现实世界的例子   当两个人见面时发生活锁   在狭窄的走廊里,每个人都在尝试   通过转移让我们礼貌   另一个通过,但他们结束了   没有一个人左右摇晃   取得任何进展,因为他们俩   反复移动的方式相同   同一时间。

     

Livelock是一种风险   一些检测和算法的算法   从僵局中恢复过来。如果超过   一个过程采取行动,僵局   检测算法可以重复   触发。这可以通过以下方式避免   确保只有一个过程(选择   随机或按优先级)采取行动。

答案 1 :(得分:71)

<强> Livelock

  

线程通常用于响应另一个线程的操作。如果   另一个线程的行动也是对另一个行动的回应   线程,然后可能导致活锁。

     

与死锁一样,活锁线程无法取得进一步进展。但是,线程未被阻止 - 它们只是太忙于相互回复以恢复工作。这相当于两个试图在走廊里互相通过的人:Alphonse向左移动让Gaston通过,而Gaston向右移动让Alphonse通过。看到他们仍在相互阻挡,Alphonse向右移动,而Gaston向左移动。他们仍然相互阻挡,等等......

livelock 死锁 之间的主要区别在于线程不会被阻止,而是将尽力相互回应。

在此图像中,两个圆圈(线程或进程)将尝试通过左右移动为另一个提供空间。但他们无法继续前进。

enter image description here

答案 2 :(得分:55)

此处的所有内容和示例均来自

操作系统:内部和设计原则
William Stallings
8º版

死锁:两个或多个进程无法继续进行的情况,因为每个进程都在等待其他进程执行某些操作。

例如,考虑两个进程P1和P2,以及两个资源R1和R2。假设每个进程都需要访问这两个资源来执行其部分功能。然后可能出现以下情况:OS将R1分配给P2,R2分配给P1。每个进程都在等待两个资源中的一个。在收购之前,它们都不会释放它已经拥有的资源 另一个资源并执行需要两个资源的功能。他们俩 进程陷入僵局

Livelock :两个或多个进程在不执行任何有用工作的情况下不断更改其状态以响应其他进程的更改的情况:

饥饿:调度程序无限期忽略可运行进程的情况;虽然它能够继续下去,但它从未被选中。

假设三个进程(P1,P2,P3)都需要定期访问资源R.考虑P1拥有资源的情况,P2和P3都被延迟,等待该资源。当P1退出其临界区时,应允许P2或P3访问R.假设操作系统授予对P3的访问权限,并且在P3完成其关键部分之前,P1再次需要访问权限。如果操作系统在P3完成后授予对P1的访问权限,并随后交替授予对P1和P3的访问权限,那么即使没有死锁情况,P2也可能无限期地被拒绝访问该资源。

附录A - 并列主题

死锁示例

如果两个进程在执行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;

活锁示例

/* 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;

[...]考虑以下事件序列:

  • P0将flag [0]设置为true。
  • P1将flag [1]设置为true。
  • P0检查标志[1]。
  • P1检查标志[0]。
  • P0将flag [0]设置为false。
  • P1将flag [1]设置为false。
  • P0将flag [0]设置为true。
  • P1将flag [1]设置为true。

这个序列可以无限延长,任何一个过程都不​​能进入其关键部分。严格地说,这是 not deadlock ,因为两个进程的相对速度的任何改变都将打破这个循环并允许进入临界区。这种情况称为活锁。回想一下,当一组进程希望进入其关键部分但没有进程能够成功时,会发生死锁。使用 livelock ,可能会有一系列执行成功,但也可以描述一个或多个执行序列,其中没有进程进入其关键部分。

答案 3 :(得分:13)

<强> DEADLOCK 死锁是任务等待的条件 无限期的条件,永远不会 满意 - 任务声明对共享的独占控制 资源 - 任务在等待其他时保留资源 要发布的资源 - 任务不能被迫重新调整资源 - 存在循环等待条件

<强>活锁 当两个或两个时,可能会出现活锁状况 更多的任务依赖于并使用一些 资源导致循环依赖 这些任务继续的条件 永远地奔跑,从而阻止所有人 来自运行的优先级任务(这些 优先级较低的任务遇到条件 叫饥饿)

答案 4 :(得分:5)

也许这两个例子说明了死锁和活锁之间的区别:

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");
        }
    }
}

示例输出:

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-活锁示例:

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
        }
    }
}

示例输出:

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
...

两个示例都强制线程以不同的顺序获取锁。 虽然死锁等待另一个锁, 活锁并没有真正等待 - 它拼命地试图获得锁而没有获得它的机会。每次尝试都会消耗CPU周期。

答案 5 :(得分:0)

假设您有线程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
         }
    }
}

因此,当线程A进入while循环并持有锁时,它将执行其必须做的工作并将commonVar设置为true。然后线程B进入,进入while循环,由于commonVar现在是true,因此它能够持有该锁。这样做,执行synchronised块,并将commonVar设置回false。现在,线程A再次获得了新的CPU窗口,它正要退出,但是线程B刚刚将其设置回了while,因此循环重复进行再次。线程可以做一些事情(因此在传统意义上没有被阻塞),但几乎没有任何作用。

提到活锁不一定一定要出现在这里,这也许也很好。我假设false块完成执行后,调度程序偏向另一个线程。在大多数情况下,我认为这是一个很难达到的期望,并且取决于引擎盖下发生的许多事情。

答案 6 :(得分:0)

我刚刚计划分享一些知识。

死锁 如果一组线程/进程中的每个线程/进程都在等待 只有该组中的另一个进程可以引起 的事件,则该线程/进程将处于死锁状态。

这里重要的是另一个过程也处于同一组中。这意味着另一进程也被阻止,没有人可以继续。

当授予进程对资源的独占访问权限时,将发生死锁。

这四个条件必须满足才能导致死锁。

  1. 互斥条件(每个资源分配给1个进程)
  2. 保持和等待条件(进程拥有资源,同时可以询问其他资源)。
  3. 没有抢占条件(以前授予的资源不能被强制删除)#此条件取决于应用程序
  4. 循环等待条件(必须是2个或更多进程的循环链,每个循环都在等待链中下一个成员拥有的资源)#它将动态发生

如果发现了这些情况,则可以说可能发生了类似僵局的情况。

LiveLock

每个线程/进程一次又一次地重复相同的状态,但是没有进一步进行。由于该过程无法进入关键部分,因此类似于死锁。但是,在死锁中,进程一直处于等待状态,但在活锁中却什么也没做,进程试图继续执行,但是进程一次又一次地重复到相同的状态。

(在死锁计算中,没有成功的可能执行序列。但是在活锁计算中,有成功的计算,但是有一个或多个执行序列,其中没有进程进入其关键部分。)

与死锁和活动锁的区别

当发生死锁时,将不会执行。但是在活锁中,会发生一些执行,但这些执行不足以进入关键部分。