为什么这个同步块似乎需要很长时间才能获得锁定?

2022-09-04 19:52:34

我是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”上没有“同步”的任何修改是否也可以锁定对象?


答案 1

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

您应该寻找两件事:

  1. 在同一对象上或对其的其他引用上的代码块(称为同步语句):synchronize

    synchronized (obj) {
     ... 
    }
    
  2. 对象本身内的同步方法

    Say is type ,那么你应该寻找这样的方法:objMyObject

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

    因为它们本质上与

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

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

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

您可以使用 jvisualvm 的“线程”选项卡Jstack 来拍摄所有线程的当前执行状态及其持有的锁的快照。如果您使用的是Android,请参阅有关如何在此处获取线程转储的答案。


答案 2

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

本文提供了有关如何查看线程转储的更多详细信息。


推荐