基于 OSGi 的应用程序 (Karaf) 中同步方法的线程死锁

Thread Deadlock on synchronized Method in OSGi based application (Karaf)

我们有一个基于 OSGi 的应用程序,它为其他 bundle 提供了一个中央服务接口,服务实现的一种方法是同步的:

MyServiceImpl implements Service {

    @Override
    public synchronized doSomething() {

    }

}

多个线程访问此服务(例如骆驼路线和网络服务调用)并同时调用 doSomething() 方法。所以,这里没什么特别的,Java 应该同步这里的方法调用。

无论如何,我们面临系统卡住的问题:线程转储告诉我们,某个线程处于 "Blocked" 状态,因为另一个线程持有服务实现对象上的锁。每个线程都在尝试调用 doSomething() 方法,而一个线程一直在等待锁被释放。

我的问题是,怎么会这样,这里没什么特别的,我想不通,为什么锁没有释放!?

只有一个锁通常不会发生死锁。只有当一个线程可以独占一个锁时,您才能获得带有一个锁的 'deadlock' 。 IE。它得到它,释放它但在另一个线程有机会之前抓住它。 Java synchronized 不是 公平 锁。

但是,最简单的死锁情况是您拥有锁 A 和锁 B。线程 T1 获得锁 A,同时线程 T2 获得锁 B。T1 做了一些工作并尝试获得锁 B。但是, B 由 T2 持有,因此它会阻塞。 T2 做了一些工作并试图获得锁 B。它被 T1 锁定,因此 T2 阻塞。

T1等待T2持有的A,T2等待T1持有的B。所以两者都无法取得进展并释放他们的锁。

所以我希望您的 doSomething() 方法实际上是通过调用其他代码在其主体中获得另一个锁。

有一个通用的解决方案,那就是创建一个锁列表,当您需要多个锁时,您总是以相同的顺序获取它们。在那种情况下,不会发生死锁。然而,问题是你很少能创建这样的列表,因为今天我们几乎总是使用别人的代码。

出于这个原因,synchronized on 方法不是一个好主意。同步是一个低级结构;您应该只在不调用外部代码的非常小的代码块上使用它。 IE。您更新数据结构但不调用。当您处于同步块中并调用其他服务时,您正在玩​​俄罗斯轮盘赌。如果您需要锁定更长的时间,请使用允许超时的 java.util.concurrency 中的 类。超时是最简单的死锁解决方案。

有很多模式可以正确使用synchronized。也就是说,这是一个非常复杂的领域。我从 Transaction Processing. A good book about Java concurrency is the Java Concurrency in Practice.

那里学到了关于锁定(以及更多)的一切知识