首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何避免在两个相互调用函数的独立类中遇到LOCK_INVERSION或LOCK_ORDER问题?

如何避免在两个相互调用函数的独立类中遇到LOCK_INVERSION或LOCK_ORDER问题?
EN

Stack Overflow用户
提问于 2022-04-09 04:56:15
回答 1查看 117关注 0票数 0

我有两个独立的类,两个类函数相互调用。我在两个类函数上都设置了同步块,但是coverity给出了有关LOCK_INVERSION和LOCK_ORDER的错误,否则可能导致死锁,不确定。

在我的生产代码上,我在coverity中得到了下面的错误。

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

示例示例:

代码语言:javascript
复制
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();
        }

    }
}
代码语言:javascript
复制
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的共享锁,但我有点担心性能,或者还有其他方法来处理这种情况?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-04-09 12:50:54

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

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

如果线程T1与另一个线程T2执行Test2.send()同时执行Test2.send(),则这种排序是可能的:

Test1.lock.

  • T2 Test2.object.

  • T2

  • T1获取Test1.lock,阻塞是因为它已经被T1持有,

  • T1试图获得Test2.object,阻塞是因为T2已经持有。

  • 两种线程都无法取得进展,这是一个死锁。

F 231

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

在代码中这样做的一种方法是更改Test2.send(),以便它首先获得Test1.lock

代码语言:javascript
复制
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.lockpublic,在本例中可能是最简单的修复。当然,如果公共数据成员是一个关注点,那么就很容易公开一个getter。

或者,您可以用保护这两个类的单个锁替换现有的一对锁。这样可以避免死锁,但可以减少并发执行的机会。如果不知道它到底在做什么(这可能是一个新问题的主题),那么这是否会对应用程序性能产生重大影响是不可能的。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71805348

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档