【问题标题】:Why does it seem to take a long time for this synchronized block to get a lock?为什么这个同步块似乎需要很长时间才能获得锁?
【发布时间】:2015-05-03 03:22:15
【问题描述】:

我是 java 多线程的新手,我有一个问题可能有些琐碎。

我必须调试第三方代码,我需要一些基本信息,以便知道在哪里寻找问题,因为代码非常大。

当以下代码运行时:

public void method()
{
   long startTime = System.currentTimeMillis();
   synchronized (obj)
   {
      log( "time:" + System.currentTimeMillis() - startTime + " ms" );
      ...
   }
}

我明白了:

11:13:12 - time: 3816 ms
...
11:14:14 - time: 0 ms

为什么要花这么长时间(3816 毫秒)来获取对象的锁? 我应该去哪里看?例如,我想一个可能的答案是寻找获取“obj”锁定的代码,即块,例如:

synchronized (obj) { ... }

或者是否有可能在没有“同步”的情况下对对象“obj”进行任何修改也可以锁定该对象?

【问题讨论】:

  • 这是你得到的第一个日志吗?
  • @CommuSoft 是的,这是我得到的第一个日志
  • @fge 对象是一个复杂对象。它用于许多不同的类并做许多不同的事情。你能解释一下你为什么问这个问题,以便我给你一个准确的答案吗?
  • @CommuSoft 如果这是 Oracle 的 JVM 并因此是 HotSpot,优化仅在 10k 执行 AFAIK 后才开始触发;因此,我怀疑 JIT 是这里的答案
  • 你在浴室锁着的门外等着。 那里需要这么长时间? 这个问题的答案与门锁的质量完全无关,而与房间里的人有关。请记住,使用锁的建议是:(1)不要花任何时间在锁上,以避免争用;(2)尽可能少的锁,以避免死锁错误。聪明的读者会注意到,这两个很好的建议实际上是相反,这会在尝试遵循它时产生问题。有趣的游戏,并发;获胜的唯一方法就是不玩。

标签: java android multithreading synchronized


【解决方案1】:

如果一个线程需要很长时间才能获得锁,那是因为其他人正在持有它。

你应该寻找两件事:

  1. synchronize 在同一个对象上或在其他对它的引用上的代码块(称为synchronized statements):

    synchronized (obj) {
     ... 
    }
    
  2. Synchronized methods 在对象本身内。

    假设objMyObject 类型,那么您应该寻找诸如以下的方法:

    public class MyObject{
        public synchronized void myMethod() {
         ...
        }
    }
    

    因为它们本质上是一样的

    public class MyObject{
        public void myMethod() {
            synchronized (this) {
             ...
            }
        }
    }
    

    所以如果一个线程正在执行obj.myMethod(),想要进入synchronized (obj) 块的线程将不得不等待,因为它们都锁定在同一个对象上。顺便说一句,这就是我强烈建议永远不要使用同步方法语法并始终锁定私有(或受保护)类成员的原因。

如果另一个线程当前正在执行这样一个块中的代码,则当前线程将被锁定,直到另一个线程完成。

您可以使用jvisualvm's Threads tabJstack 对所有线程的当前执行状态和它们持有的锁进行快照。如果您使用的是 android,请参阅this 回答,了解如何在那里获取线程转储。

【讨论】:

  • @dritan 是的,当然。我现在已经添加了这个细节。为了避免这个问题,我强烈建议您避免声明同步方法,而是在私有类成员上同步。
  • 您可以使用 jvisualvm 的 Threads 选项卡或 Jstack 对所有线程的当前执行状态和它们所持有的锁进行快照。 ==> 我正在安卓上开发。您对这种环境有什么建议吗?
  • @dritan 我没有,但堆栈溢出确实 - stackoverflow.com/questions/13589074/…
  • 刚刚注意到答案的第 2 点是相关的。还有其他线程调用的其他同步方法
【解决方案2】:

作为 jdk 一部分的 jstack 实用程序可以帮助解决这个问题。 -l(长列表)选项将打印各个线程持有的所有锁。如果您可以在问题中捕获您的程序,那么您可以找到另一个持有锁的线程。为此,您可以找到您的线程,查看它正在等待的条件对象,然后在堆栈跟踪的其余部分中搜索该条件对象。

这个article 有更多关于如何查看线程转储的详细信息。

【讨论】:

  • 问题标记为 [android],因此 JDK 实用程序可能不是最有用的。
  • @fadden 发布此答案时未标记“android”
【解决方案3】:

您需要检查以下内容:

  • 您的 obj 类中是否有任何方法/块,在此同步。如果是,那么必须有多个线程,其中一个是您的上述代码,而另一个可能正在使用相同 obj 的方法。
  • 你在哪里共享 obj?如果它被多个类共享,则检查谁锁定了同一个 obj。

【讨论】:

  • 您还应该查看obj 是否不仅与synchronized 一起使用,而且还与可以交互的notifylock 一起使用。应该避免混用这两种同步模式,但是在调试第三方代码时,你应该做好最坏的打算。
  • 是的,但请注意,如果没有通知,那么 OP 的输出将永远不会打印任何内容。同样很明显,如果您使用同步,那么您将使用通知/等待,但是正如您所说的那样不能确定..
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-06-17
  • 2020-11-08
  • 2012-04-29
  • 2016-03-18
  • 2017-09-22
  • 2013-02-06
相关资源
最近更新 更多