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

我是java中的multithreading新手,我有一些问题可能会发现很简单。

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

运行以下代码时:

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) { ... } 

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

如果它需要一个很长的线程来获得锁定,那是因为其他人正在持有它。

你应该寻找两件事:

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

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

    假设objMyObjecttypes,那么你应该寻找这样的方法:

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

    因为它们基本上是一样的

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

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

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

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

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

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

您需要检查以下内容:

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