Java 修复混乱代码中的死锁

Java 修复混乱代码中的死锁,java,multithreading,synchronization,locking,Java,Multithreading,Synchronization,Locking,我正在修改一些非常复杂的代码,我需要在上面添加我自己的同步 然而,现有代码有十几个(如果不是更多的话)不同的锁,我的代码需要调用它的一些方法。我真的不知道锁的获取顺序,也不能真正控制它 所以,我的问题是,如果我用一个锁替换所有不同的锁,会发生什么?除了牺牲粒度之外,还有什么其他问题需要注意吗 谢谢 如果没有看到代码,很难说会发生什么。 如果在其中一个锁中,它试图获取另一个锁,则可能会遇到死锁问题。另外,在等待获取单个锁与多个锁时,它可能会减慢应用程序的速度。如果您更改所有的同步的块(和方法),以

我正在修改一些非常复杂的代码,我需要在上面添加我自己的同步

然而,现有代码有十几个(如果不是更多的话)不同的锁,我的代码需要调用它的一些方法。我真的不知道锁的获取顺序,也不能真正控制它

所以,我的问题是,如果我用一个锁替换所有不同的锁,会发生什么?除了牺牲粒度之外,还有什么其他问题需要注意吗


谢谢

如果没有看到代码,很难说会发生什么。
如果在其中一个锁中,它试图获取另一个锁,则可能会遇到死锁问题。另外,在等待获取单个锁与多个锁时,它可能会减慢应用程序的速度。

如果您更改所有的
同步的
块(和方法),以及所有其他块结构,我认为您应该会没事——最坏的情况是,您的应用程序会退化为串行执行。但如果你只改变其中的一些,你可能会陷入僵局。考虑一个场景,其中两个线程各自获取多个锁:

Thread 1:
    synchronized A
        synchronized B

Thread 2:
    synchronized B
        synchronized C
这里没有死锁的风险,但是如果用新的通用锁替换
A
C
(但不是
B
),那么您将拥有:

Thread 1:
    synchronized L
        synchronized B

Thread 2:
    synchronized B
        synchronized L
。。。这是典型的死锁案例

考虑另一种情况,在这种情况下,锁本身不提供死锁,而是死锁一个阻塞类,如CountDownLatch:

Thread 1:
    synchronized A
        latch L.countDown()

Thread 2:
    synchronized B
        latch L.await()

在这种情况下,将两个
synchronized
块更改为锁定在公共锁上不会导致它们之间的死锁,但如果线程2首先获得锁,则会导致死锁:它将等待闩锁的倒计时,因为线程1在其
synchronized
入口点被阻塞,所以它永远不会到来。这个示例也适用于其他阻塞结构:信号量、阻塞队列等。

我认为没有任何替代方法可以正确分析代码。它之所以可怕,可能是因为其他所有不得不修改它的人都做了与您完全相同的事情,并在适当的分析中畏缩不前

编写一些日志代码来澄清锁定应该相当简单。一旦你可以解开图层并有一个清晰的图片,用一个现代化的锁(如
ReadWriteLock
或类似的锁)来替换整个批次应该比较简单


您可能会发现,利用这个机会添加一些测试代码来以受控的方式执行日志记录非常有用。这是对任何复杂代码的一个非常有用的补充。

您能发布代码片段吗?不,它大约有3000多行:-请注意,这不是我的代码……据我所知,如果所有方法都需要原子性,那么需要一个锁,如果需要公平性,那么每个线程都将由处理器按照它们查找资源的顺序提供服务,您可以查看java.util.concurrent.lock(这保证了公平性,但也带来了间接费用)。在订购java之前,请阅读和,以便更好地理解锁和发生的事情。上周我花了大量时间研究了不属于我的多线程代码,我可以告诉你,这并不容易,你可能需要花时间真正研究这段代码。我会犹豫是否盲目地更换锁而不了解这些代码w代码目前运行正常。幸运的是JVM在检测死锁方面非常出色,因此如果您还没有,您应该学习如何捕获和读取线程转储。最后,在没有看到代码的情况下,我会说您肯定更喜欢java.util.concurrent中的工具,而不是任何自行开发的锁定解决方案。我的想法是将所有锁合并到一个锁中,这将消除牺牲粒度的死锁问题。我想知道我是否是对的。@Albus Dumbledore如果锁定是唯一一种正在发生的同步,那么你可能是对的。要知道,牺牲粒度,也可能牺牲性能并引入机会主义感谢Alex。我对可能出现的性能问题没有意见,事实是,大多数方法花费的时间非常非常非常少(例如,不到几毫秒)同时调用太多线程的可能性很小,但另一方面,代码的复杂性非常高,但我不明白为什么他们使用这么多锁引入了更高的复杂性。非常感谢你证实我的想法。很好的一点是倒计时锁-如果你更换锁,你还必须当然,如果你在等待一个公共资源时,如果还没有这样做,你就可以释放锁。谢谢@yshavit!不过,我不理解最后一个例子。锁对我来说是新事物,所以如果我在代码中没有看到锁,我就不必麻烦了,是吗?或者你说的锁是更抽象的东西吗?谢谢!@AlbusDumblemore正如@Alex指出的,实际上是任何可以阻止的资源。
java.util.concurrent
java.util.concurrent.locks
中的许多类都适用,但原则上可能是其他资源(比如一段代码创建文件,另一段代码等待该文件存在)。