为什么这个synchronized块好像要很久才能拿到锁?

Why does it seem to take a long time for this synchronized block to get a lock?

我是 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" 上的任何修改而没有 "synchronized" 也可以锁定对象?

您需要检查以下内容:

  • 你的objclass里面有没有method/block,在这上面同步的。如果是,则必须有多个线程,其中一个是您上面的代码,而其他线程可能正在使用相同对象的方法。
  • 你在哪里共享obj?如果它被多个 classes 共享,然后检查谁锁定了同一个对象。

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

article 包含有关如何查看线程转储的更多详细信息。

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

你应该寻找两件事:

  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) 块的线程将不得不等待,因为它们都锁定在同一个对象上。顺便说一句,这就是为什么我 强烈建议 永远不要使用同步方法语法,并且始终锁定私有(或受保护)class 成员的原因。

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

您可以使用 jvisualvm's Threads tab or Jstack to take snapshots of the current execution state of all threads and the locks they're holding. If you're on android, see this 回答如何在那里获取线程转储。