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

dri*_*tan 12 java multithreading android synchronized

我是java中的多线程新手,我有一个问题,有些人可能会发现微不足道.

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

运行以下代码时:

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

我明白了:

11:13:12 - time: 3816 ms
...
11:14:14 - time: 0 ms
Run Code Online (Sandbox Code Playgroud)

为什么需要这么长时间(3816毫秒)才能获得对象的锁定?我应该在哪里看?例如,我想可能的答案是寻找获取"obj"锁定的代码,例如:

synchronized (obj) { ... }
Run Code Online (Sandbox Code Playgroud)

或者是否可能在没有"synchronized"的对象"obj"上进行任何修改也可以锁定对象?

Mal*_*alt 11

如果获取锁定的线程很长,那是因为其他人正在持有它.

你应该找两件事:

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

    synchronized (obj) {
     ... 
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 对象本身内的同步方法.

    obj是类型MyObject,那么你应该寻找这样的方法:

    public class MyObject{
        public synchronized void myMethod() {
         ...
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

    因为它们基本相同

    public class MyObject{
        public void myMethod() {
            synchronized (this) {
             ...
            }
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

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

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

您可以使用jvisualvm的Threads选项卡Jstack来获取所有线程的当前执行状态及其持有的锁的快照.如果您使用的是Android,请参阅答案,了解如何在那里获取线程转储.


Mar*_*ano 5

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

文章在如何看待线程转储更详细的信息.