Почему, похоже, для этого синхронизированного блока требуется много времени, чтобы получить блокировку?

Я новичок в многопоточности в 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» без «синхронизированного» может также блокировать объект?

Solutions Collecting From Web of "Почему, похоже, для этого синхронизированного блока требуется много времени, чтобы получить блокировку?"

Если для получения блокировки требуется поток, который длится долго, это происходит потому, что кто-то еще удерживает его.

Вы должны искать две вещи:

  1. Блоки кода, которые synchronize на одном и том же объекте или на других ссылках на него (так называемые синхронизированные операторы ):

     synchronized (obj) { ... } 
  2. Синхронизированные методы внутри самого объекта.

    Скажем, obj имеет тип MyObject , тогда вы должны искать такие методы, как:

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

    Потому что они по существу те же, что и

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

    Поэтому, если поток выполняет obj.myMethod() , потоки, которые хотят ввести synchronized (obj) блок, должны будут ждать, поскольку все они блокируются на одном и том же объекте. Это, кстати, является причиной, по которой я настоятельно рекомендую никогда не использовать синхронный синтаксис метода и всегда блокировать доступ к частному (или защищенному) классу.

Если другой поток в настоящее время выполняет код внутри такого блока, текущий поток будет заблокирован до тех пор, пока не закончится другой поток.

Вы можете использовать вкладку «Темы» jvisualvm или Jstack, чтобы делать снимки текущего состояния выполнения всех потоков и блокировок, которые они удерживают. Если вы находитесь в android, см. Этот ответ о том, как получить дамп потока.

Утилита jstack, которая является частью jdk, может помочь в этом. Опция -l (длинный список) будет печатать все блокировки, принадлежащие различным потокам. Если вы можете поймать свою программу посреди проблемы, вы можете найти другой поток, удерживающий блокировку. Вы делаете это, находя свой поток, видя, какой объект условия он ожидает, затем просматривая остальные трассировки стека для этого объекта условия.

В этой статье содержится более подробная информация о том, как посмотреть дамп потока.

Вам необходимо проверить следующее:

  • Есть ли какой-либо метод / блок в вашем классе obj, который синхронизируется по этому вопросу. Если да, то должно быть несколько потоков, где один из них – ваш вышеприведенный код, в то время как другие могут использовать методы того же obj.
  • Где все вы обмениваетесь obj? Если он используется несколькими классами, то проверьте, кто блокирует его на том же объекте.