如何避免两个独立的 class 函数相互调用时出现 LOCK_INVERSION 或 LOCK_ORDER 问题?
How to avoid LOCK_INVERSION or LOCK_ORDER problem in two independent class calling each other function?
我有两个独立的 类 并且两个 类 函数都在互相调用。我已经将同步块放在两个 类 函数上,但是 coverity 给出了关于 LOCK_INVERSION 和 LOCK_ORDER 的错误,或者它可能导致死锁,不确定。
我的生产代码的覆盖率低于错误。
获取锁 'lockname' 与在别处建立的锁顺序冲突。
示例:
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
.
如果线程 T1 在另一个线程 T2 执行 Test2.send()
的同时执行 Test1.sendUpdate()
,则此排序是可能的:
- 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 中的锁是 recursive.
以上代码要求 Test1.lock
为 public
,这可能是本例中最简单的修复方法。如果 public 数据成员是一个问题,那么公开 getter 当然很容易。
或者,您可以将现有的一对锁替换为一个保护两者的锁 类。这样可以避免死锁,但会减少并发执行的机会。如果不进一步了解它的实际作用(这可能是一个新问题的主题),就不可能说这是否会显着影响应用程序性能。
我有两个独立的 类 并且两个 类 函数都在互相调用。我已经将同步块放在两个 类 函数上,但是 coverity 给出了关于 LOCK_INVERSION 和 LOCK_ORDER 的错误,或者它可能导致死锁,不确定。
我的生产代码的覆盖率低于错误。
获取锁 'lockname' 与在别处建立的锁顺序冲突。
示例:
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
.
如果线程 T1 在另一个线程 T2 执行 Test2.send()
的同时执行 Test1.sendUpdate()
,则此排序是可能的:
- 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 中的锁是 recursive.
以上代码要求 Test1.lock
为 public
,这可能是本例中最简单的修复方法。如果 public 数据成员是一个问题,那么公开 getter 当然很容易。
或者,您可以将现有的一对锁替换为一个保护两者的锁 类。这样可以避免死锁,但会减少并发执行的机会。如果不进一步了解它的实际作用(这可能是一个新问题的主题),就不可能说这是否会显着影响应用程序性能。