如何解决死锁和活锁有什么区别?
| 有人可以用示例解释一下(死锁)和活锁之间的区别吗?解决方法
取自http://en.wikipedia.org/wiki/Deadlock:
在并发计算中,死锁是一种状态,其中一组动作中的每个成员都在等待其他成员释放锁
活锁类似于死锁,
除了
活锁中涉及的过程
关于一个不断变化
另一个,没有进展。活锁是
资源匮乏的特殊情况;
一般定义只说明
一个特定的过程不是
进步。
一个真实的例子
当两个人见面时发生活锁
在狭窄的走廊上,每个尝试
放在一边让自己彬彬有礼
另一遍,但最终
左右摇摆而没有
取得进展,因为他们俩
以相同的方式在
同时。
活锁存在以下风险
一些算法可以检测并
从僵局中恢复。如果超过
一个过程采取行动,僵局
检测算法可以重复
触发。这可以避免
确保只有一个流程(选择
随机或优先)采取行动。
, 活锁
一个线程通常会响应另一个线程的操作而行动。如果
另一个线程的动作也是对另一个线程的动作的响应
线程,则可能导致活锁。
与死锁一样,活动锁定的线程无法取得进一步的进展。但是,线程没有被阻塞-它们只是太忙于彼此响应而无法恢复工作。这相当于两个人试图在走廊中互相经过:阿方斯(Alphonse)向左移动以让加斯顿(Gaston)通过,而格斯顿(Gaston)向右移动以让Alphonse通过。看到他们仍然互相阻挡,阿方斯(Alphonse)向右移动,而加斯顿(Gaston)向左移动。他们仍然互相阻碍,依此类推...
活锁和死锁之间的主要区别在于,线程不会被阻塞,而是会尝试不断地相互响应。
在此图像中,两个圆圈(线程或进程)都将尝试通过左右移动来给另一个空间。但是他们不能继续前进。
,这里的所有内容和示例均来自
操作系统:内部和设计原则
威廉·斯托林斯
8º版
死锁:一种情况,其中两个或多个进程无法进行,因为每个进程都在等待另一个进程在做某事。
例如,考虑两个进程P1和P2,以及两个资源R1和R2。假设每个进程都需要访问两个资源才能执行其部分功能。然后可能出现以下情况:OS将R1分配给P2,将R2分配给P1。每个进程都在等待两种资源之一。在获得之前,两者都不会释放它已经拥有的资源
另一个资源,并执行需要两个资源的功能。他们俩
进程陷入僵局
活锁:两个或多个进程在不做任何有用工作的情况下,不断响应其他进程的更改而更改其状态的情况:
饥饿:调度程序无限期地忽略可运行进程的情况;尽管它能够继续进行,但从未选择。
假设三个进程(P1,P2,P3)每个都需要定期访问资源R。请考虑以下情况:P1拥有该资源,并且P2和P3都被延迟,等待该资源。当P1退出其关键部分时,应该允许P2或P3访问R。假定OS授予对P3的访问权限,并且P1在P3完成其关键部分之前再次需要访问。如果OS在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将标志[0]设置为true。
P1将flag [1]设置为true。
P0检查标志[1]。
P1检查标志[0]。
P0将标志[0]设置为false。
P1将flag [1]设置为false。
P0将标志[0]设置为true。
P1将flag [1]设置为true。
此序列可以无限期扩展,并且任何过程都不能进入其关键部分。严格来说,这不是死锁,因为这两个过程的相对速度的任何改变都将打破此循环并允许一个进入关键部分。这种情况称为活锁。回想一下,当一组进程希望进入其关键部分而没有成功的进程时,就会发生死锁。使用livelock,可能会有成功的执行序列,但是也有可能描述一个或多个执行序列,其中没有进程进入其关键部分。
, 僵局
死锁是任务等待的条件
无限期地针对永远不会发生的情况
满意
-任务声称对共享拥有独占控制
资源
-任务在等待其他资源时拥有资源
要释放的资源
-不能强迫任务重新分配资源
-存在循环等待条件
活锁
当两个或两个以上时,可能会出现活锁情况
更多的任务取决于并使用一些
导致循环依赖的资源
这些任务继续的条件
永远运行,从而阻止所有下层
优先级任务从运行(这些
优先级较低的任务遇到条件
叫做饥饿)
, 也许这两个示例向您说明了死锁和活锁之间的区别:
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周期。
,假设您有线程A和线程B。它们在同一个对象上都为“ 6”,并且在此块中有一个全局变量,它们都在更新;
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
块,并将ѭ9sets设置回false
。现在,线程A再次获得它的新CPU窗口,它即将退出while
循环,但是线程B刚刚将其设置回false
,因此该循环再次重复。线程可以执行某些操作(因此在传统意义上不会被阻塞),但几乎没有任何作用。
可能还需要提及的是,活锁不一定必须出现在此处。我假设synchronised
块完成执行后,调度程序偏向另一个线程。在大多数情况下,我认为这是一个很难达到的期望,它取决于引擎盖下发生的许多事情。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。