Java中的Livelock和Deadlock
下面的文章讨论java中的livelock和deadlock状态,它们是如何发生的,以及如何避免它们。
活锁
Java中的Livelock是一种递归条件,其中两个或者多个线程不断重复一段特定的代码。
当一个线程继续响应另一个线程,而另一个线程也在执行相同的操作时,就会发生Livelock。
分解起来,我们可以用以下几点来概括:
一个线程正在响应另一个线程的操作,而另一个线程也在响应前一个线程,则可能会发生livelock。
Livelock线程无法继续前进。
线程没有被阻塞;它们只是忙于相互响应。
Livelock也被称为资源匮乏的一种特殊情况
让我们通过把它与现实世界的情况联系起来来理解这个概念。考虑两辆车在一座窄桥的对面。一次只有一辆车能通过这座桥。两辆车的司机都很有礼貌,都在等对方先过桥。他们都会按喇叭来回应对方,让他们知道他们希望对方先通过。然而,两人都不过桥,一直互相按喇叭。这种情况类似于livelock。
现在让我们用一些代码来尝试这个真实的场景:
第一辆车过桥等级:
public class Car1 { private boolean honking = true; public void passBridge(Car2 car2) { while (car2.hasPassedBridge()) { System.out.println("Car1 waiting to pass the bridge"); try { Thread.sleep(1000); } catch (InterruptedException ex) { ex.printStackTrace(); } } System.out.println("Passed bridge"); this.honking= false; } public boolean hasPassedBridge() { return this.honking; } }
等待过桥的第二辆车的等级:
public class Car2 { private boolean honking = true; public void passBridge(Car1 car1) { while (car1.hasPassedBridge()) { System.out.println("Car 2 is waiting to pass the bridge!"); try { Thread.sleep(1000); } catch (InterruptedException ex) { ex.printStackTrace(); } } System.out.println("Car 2 has passed the bridge!"); this.honking = false; } public boolean hasPassedBridge() { return this.honking; } }
主要测试等级:
public class BridgeCheck { static final Car2 car2 = new Car2(); static final Car1 car1 = new Car1(); public static void main(String[] args) { Thread t1 = new Thread(new Runnable() { public void run() { car2.passBridge(car1); } }); t1.start(); Thread t2 = new Thread(new Runnable() { public void run() { car1.passBridge(car2); } }); t2.start(); } }
输出:
这将导致非终止循环。
死锁
死锁与livelock有点不同。死锁是一种状态,每个成员都在等待其他成员释放锁。
当一个线程正在等待另一个线程获取的对象锁,而第二个线程正在等待第一个线程获取的对象锁时,可能会出现这种情况。因为两个线程都在等待对方释放锁,所以这种情况称为死锁。
图:死锁状态
让我们看一个发生死锁的场景:
public class DeadlockExample{ private static String A = "Something A"; private static String B = "Something B"; public void someFunction(){ synchronized(A){//Jan deadlock here synchronized(B){ //function does some work here } } } public void someOtherFunction(){ synchronized(B){//Jan deadlock here synchronized(A){ //the function does something here } } } }
考虑两个线程T1和T2,T1获取A并等待B来完成其功能。然而,T2获得B并等待A来完成它自己的功能。这里T1和T2正在等待被其他线程锁定的资源。因此,这是一个死锁场景。
共同避免僵局
第一个建议是避免同时使用多线程,但这在许多情况下可能并不实用。所以这个解决方案不太明智。
分析并确保在预先访问资源时没有锁。
在上面的编码示例中,为了避免死锁,只需管理资源访问的顺序(a和B访问的顺序)。
避免同时持有多个锁,以防我们必须始终以相同的顺序获取锁。
在持有锁时避免执行外来代码。
尝试使用可中断锁,这样即使遇到死锁,锁也可以被中断,并且可以毫无问题地执行进程。