如何避免两个独立类相互调用函数LOCK_INVERSION或LOCK_ORDER问题?



我有两个独立的类,两个类函数互相调用。我已经在两个类函数上放置了同步块,但是覆盖率给出了有关LOCK_INVERSION和LOCK_ORDER的错误,或者可能会导致死锁,不确定。

我的生产代码的覆盖率低于错误。

获取锁"锁名"与在其他地方建立的锁顺序冲突。

示例示例:

package multithreading;
public class Test1 implements Runnable {
private static Test1 instance = null;
public static synchronized Test1 getInstance() {
if (instance == null) {
instance = new Test1();
return instance;
} else {
return instance;
}
}
private Object lock = new Object();
public void sendUpdate() {
synchronized (lock) {
try {
Thread.sleep(3000l);
System.out.println(getClass().getSimpleName() + "sendUpdate");
Test2.getInstance().send();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

public void sendUpdate1() {
synchronized (lock) {
try {
Thread.sleep(3000l);
System.out.println(getClass().getSimpleName() + "sendUpdate1");
Test2.getInstance().send();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

public static void main(String[] args) {
Thread t = new Thread(Test1.getInstance());
t.start();
}
@Override
public void run() {
while (true) {
this.sendUpdate();
}
}
}
package multithreading;
public class Test2 implements Runnable {
private static Object object = new Object();
private static Test2 instance = null;
public static synchronized Test2 getInstance() {
if (instance == null) {
instance = new Test2();
return instance;
} else {
return instance;
}
}

public void send1() {
synchronized (object) {
try {
Thread.sleep(3000l);
System.out.println(getClass().getSimpleName() + "send1");
Test1.getInstance().sendUpdate();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void send() {
synchronized (object) {
try {
Thread.sleep(3000l);
System.out.println(getClass().getSimpleName() + "send");
Test1.getInstance().sendUpdate();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Thread t = new Thread(Test2.getInstance());
t.start();
}
@Override
public void run() {
while (true) {
Test2.getInstance().send1();
}
}
}
Analysis summary report:
------------------------
Files analyzed                 : 2 Total
Java (without build)       : 2
Total LoC input to cov-analyze : 90
Functions analyzed             : 15
Paths analyzed                 : 71
Time taken by analysis         : 00:00:18
Defect occurrences found       : 1 LOCK_INVERSION

我认为这可能会导致死锁问题,但想了解如何在两个相互调用函数的独立类中基本上处理同步。

我们如何保持两个类之间的锁定顺序? 我应该使用基于 ENUM 的共享锁,但我有点担心性能还是有其他方法可以处理这种情况?

Coverity 报告的原因是代码包含潜在的死锁错误。 具体说来:

  • Test1.sendUpdate()获取Test1.lock,然后调用Test2.send(),后者获取Test2.object。 同时
  • Test2.send()获取Test2.object,然后调用Test1.sendUpdate(),获取Test1.lock

如果线程 T1Test1.sendUpdate()与另一个线程 T2 同时执行Test2.send(),则可以进行以下排序:

  • T1 收购Test1.lock.
  • T2 收购Test2.object.
  • T2 试图获取Test1.lock,并阻止,因为它已经被 T1 持有。
  • T1 试图获取Test2.object,并阻止,因为它已经被 T2 持有。
  • 两个线程都无法取得进展;这是一个僵局。

当单个线程必须同时持有多个锁时,防止死锁的典型方法是确保获取锁的顺序始终一致。 在这里,这意味着要么总是首先获取Test1.lock,要么总是首先获取Test2.object

在代码中执行此操作的一种方法是更改Test2.send()以便它首先获取Test1.lock

public void send() {                 // in class Test2
Test1 test1 = Test1.getInstance();
synchronized (test1.lock) {      // Test1.lock first  <-- ADDED
synchronized (object) {      // Test2.object second
try {
...                  // same code as before
test1.sendUpdate();
...
}
}
}
}

send()内部,对sendUpdate()的调用将第二次获得Test1.lock。 这没关系,因为 Java 中的锁是递归的。

上面的代码要求Test1.lockpublic,并且可能是在这种情况下最简单的修复。 当然,如果公共数据成员是一个问题,则很容易公开getter。

或者,您可以将现有的锁对替换为保护这两个类的单个锁。 这将不会死锁,但可能会减少并发执行的机会。 如果不更多地了解它的实际作用,就不可能说这是否会显着影响应用程序性能(这可能是一个新问题的主题)。

最新更新