2016-12-23 7 views
6

Я пытаюсь понять, что делает Locked ownable synchronizers в дампе потока?Что такое "Заблокированные управляемые синхронизаторы" в дампе потока?

Я начал использовать ReentrantReadWriteLock есть поток в WAITING состоянии, ожидая ReentrantReadWriteLock$FairSync в списке «заблокированные ownable синхронизаторов» другого потока в WAITING государства (ThreadPoolExecutor).

Я не мог найти много информации об этом. Это какие-то замки, «перешедшие» на нить? Я пытаюсь выяснить, откуда приходит мой тупик, и я не вижу ни одного потока, активно блокирующего его (т. Е. Соответствующего - locked <0x...> в любой трассировке стека).

ответ

4

TL; DR: блокировки записи появляются в списке "ownable синхронизаторов" блокировки чтения не.

В итоге у меня появился следующий MVCE, чтобы попытаться понять, что такое «управляемый синхронизатор». Идея состояла в том, чтобы блокировать/разблокировать блокировки/разблокировки повторных попыток чтения/записи и видеть влияние на разные дампы потоков при разных таймингах (взятых в jVisualVM, в то время как проект Eclipse был приостановлен в точках прерывания в определенных строках).

Вот код (в пакете "замок"):

public class LockTest { 

    static ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true); 

    public static void main(String[] args) { 
     lock.readLock().lock(); 
     System.out.println(Thread.currentThread().getName()+": read hold "+lock.getReadHoldCount()+" read lock "+lock.getReadLockCount()); 
     new Th().start(); 
     synchronized (LockTest.class) { 
      try { LockTest.class.wait(); } catch (InterruptedException e) { } 
     } 
     lock.readLock().unlock(); 
     System.out.println(Thread.currentThread().getName()+": unlocked read lock. Read hold "+lock.getReadHoldCount()+" read lock "+lock.getReadLockCount()+". Getting write lock"); 
     lock.writeLock().lock(); 
     System.out.println(Thread.currentThread().getName()+": got write lock. Unlocking (=>Thread dump #3)"); // Take thead dump #3 here ("main" has a write lock, "other" has died) 
     lock.writeLock().unlock(); 
    } 

    static class Th extends Thread { 
     Th() { super("other"); } 

     public void run() { 
      System.out.println(Thread.currentThread().getName()+": read hold "+lock.getReadHoldCount()+" read lock "+lock.getReadLockCount()); 
      if (!lock.writeLock().tryLock()) 
       System.out.println(Thread.currentThread().getName()+": cannot lock write"); 
      else { 
       System.out.println(Thread.currentThread().getName()+": lock write taken"); 
       lock.writeLock().unlock(); 
      } 
      System.out.println(Thread.currentThread().getName()+": trying to unlock read lock"); 
      try { 
       lock.readLock().unlock(); 
       System.out.println(Thread.currentThread().getName()+": successfully unlocked read lock. Read hold "+lock.getReadHoldCount()+" read lock "+lock.getReadLockCount()); 
      } catch (IllegalMonitorStateException e) { 
       System.out.println(Thread.currentThread().getName()+": cannot unlock read lock: "+e.getMessage()); 
      } 
      synchronized (LockTest.class) { 
       System.out.println(Thread.currentThread().getName()+": notifying write lock take (=>Thread dump #1)"); 
       LockTest.class.notify(); // Take thead dump #1 here ("main" has a read lock) 
      } 
      System.out.println(Thread.currentThread().getName()+": locking write lock"); 
      lock.writeLock().lock(); 
      System.out.println(Thread.currentThread().getName()+": unlocking write lock (=>Thread dump #2)"); // Take thead dump #2 here ("other" has a write lock) 
      lock.writeLock().unlock(); 
     } 
    } 
} 

Вот результат:

main: read hold 1 read lock 1 
other: read hold 0 read lock 1 
other: cannot lock write 
other: trying to unlock read lock 
other: cannot unlock read lock: attempt to unlock read lock, not locked by current thread 
other: notifying write lock take (=>Thread dump #1) 
other: locking write lock 
main: unlocked read lock. Read hold 0 read lock 0. Getting write lock 
other: unlocking write lock (=>Thread dump #2) 
main: got write lock. Unlocking (=>Thread dump #3) 

Теперь нить свалки.

Сброс резьбы # 1 берется, когда резьба "main" получает блокировку чтения. Как мы можем видеть, нет «ownable синхронизатор» не принадлежит резьбе:

"main" prio=10 tid=0x00007fea5c00d000 nid=0x1866 in Object.wait() [0x00007fea65bd5000] 
    java.lang.Thread.State: WAITING (on object monitor) 
    at java.lang.Object.wait(Native Method) 
    - waiting on <0x00000007acf62620> (a java.lang.Class for lock.LockTest) 
    at java.lang.Object.wait(Object.java:503) 
    at lock.LockTest.main(LockTest.java:14) 
    - locked <0x00000007acf62620> (a java.lang.Class for lock.LockTest) 

    Locked ownable synchronizers: 
    - None 

"other" prio=10 tid=0x00007fea5c0e0800 nid=0x1883 at breakpoint[0x00007fea3abe8000] 
    java.lang.Thread.State: RUNNABLE 
    at lock.LockTest$Th.run(LockTest.java:46) 
    - locked <0x00000007acf62620> (a java.lang.Class for lock.LockTest) 

    Locked ownable synchronizers: 
    - None 

Thread свалка # 2 принимается после того, как нить «другой» взял блокировку записи. Оказывается, в «ownable синхронизаторов»:

"main" prio=10 tid=0x00007fea5c00d000 nid=0x1866 waiting on condition [0x00007fea65bd5000] 
    java.lang.Thread.State: WAITING (parking) 
    at sun.misc.Unsafe.park(Native Method) 
    - parking to wait for <0x00000007acf63278> (a java.util.concurrent.locks.ReentrantReadWriteLock$FairSync) 
    at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186) 
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:834) 
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:867) 
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1197) 
    at java.util.concurrent.locks.ReentrantReadWriteLock$WriteLock.lock(ReentrantReadWriteLock.java:945) 
    at lock.LockTest.main(LockTest.java:18) 

    Locked ownable synchronizers: 
    - None 

"other" prio=10 tid=0x00007fea5c0e0800 nid=0x1883 at breakpoint[0x00007fea3abe8000] 
    java.lang.Thread.State: RUNNABLE 
    at lock.LockTest$Th.run(LockTest.java:51) 

    Locked ownable synchronizers: 
    - <0x00000007acf63278> (a java.util.concurrent.locks.ReentrantReadWriteLock$FairSync) 

Thread свалка # 3 принимается после того, как нити «другой» выпустила блокировку записи (и умер), и нити «основной» взял его:

"main" prio=10 tid=0x00007fea5c00d000 nid=0x1866 at breakpoint[0x00007fea65bd5000] 
    java.lang.Thread.State: RUNNABLE 
    at lock.LockTest.main(LockTest.java:19) 

    Locked ownable synchronizers: 
    - <0x00000007acf63278> (a java.util.concurrent.locks.ReentrantReadWriteLock$FairSync) 

Таким образом, блокировки записи появятся в списке «заблокированных управляемых синхронизаторов», когда блокировки чтения не будут. Несмотря на то, что getReadHoldCount() показывает количество блокировок чтения, выполненных текущим потоком, чтение «блокировка», похоже, не относится к определенному потоку и поэтому отсутствует в списке. И это затрудняет отладку блокировок (или, скажем, «не так просто, как с помощью jVisualVM»).

EDIT: Для того, чтобы помочь выяснить, копирование/ошибку вставки с замками, принятых и не выпустили, например, в:

myLock.readLock().lock(); 
try { 
    // ... 
} finally { 
    myLock.readLock().lock(); // Oops! Should be "unlock()" 
} 

вы можете использовать следующую командную строку Linux в корне каталога источника:

find . -name '*.java' -exec grep -Hn 'myLock.readLock().lock();' {} \; | wc -l 

покажет, сколько чтения замки приняты, а также:

find . -name '*.java' -exec grep -Hn 'myLock.readLock().unlock();' {} \; | wc -l 

отображает, сколько считываемых замков выпущено. Если числа не совпадают, удалите | wc -l, чтобы показать подробную информацию о имени файла (grep -H) и номер строки (grep -n).

+0

Хорошая работа! Спасибо, что поделились –

+0

Я еще не приму своего ответа, если кто-нибудь придумает более подробное объяснение. – Matthieu

+1

Замечательный анализ. Выделите резюме/заключение либо в начале, либо в конце. –

4

От Java 7 documentation:

ownable синхронизатора является синхронизатор, который может быть исключительно принадлежит потоку и использует AbstractOwnableSynchronizer (или его подкласс), чтобы осуществить его свойство синхронизации. ReentrantLock и ReentrantReadWriteLock - это два примера управляемых синхронизаторов , предоставляемых платформой.

+0

Значит, замки в этом списке принадлежат теме? Как это возможно, если я не вижу никакой информации о '- locked <0x...>' в дампе потока? – Matthieu

1

Правильное использование ReentrantLock не так просто, как кажется. У этого есть несколько подводных камней. Если мы говорим о тупиков я думаю, что вы должны знать:

1.

Основное объяснение, которое мы нашли в этот момент связан с использования замка ReetrantLock READ. Обычно считывающие замки не являются , предназначенные для определения понятия собственности. Поскольку нет записи , в которой поток содержит блокировку чтения, это, по-видимому, предотвращает логику детектора блокировки HotSpot JVM для обнаружения взаимоблокировки с блокировками чтения.

Некоторые улучшения были реализованы с тех пор, но мы видим, что JVM до сих пор не может обнаружить этот особый сценарий взаимоблокировки.

Это от хорошей статьи «Java concurrency: the hidden thread deadlocks»

Если у вас есть доступ к исходному коду getReadHoldCount() метода может помочь в следственных тупиках.

2. Правильное обновление с блокировкой чтения на блокировку записи - "Java ReentrantReadWriteLocks - how to safely acquire write lock?"

+0

+1 для указания, что считывающие блокировки не отмечены «принадлежащими» потоком. Я буду исследовать различные варианты использования, чтобы увидеть, как это может относиться к моей проблеме, заставляя дампы потоков после захвата блокировки. – Matthieu

+0

@Matthieu добро пожаловать! Если вы найдете что-то, пожалуйста, поделитесь каким-то кодом, было бы интересно –

+0

Я только что провел несколько тестов с дампами потоков в ответе. Вкратце: блокировки чтения не отображаются в списке «доступные синхронизаторы». – Matthieu

 Смежные вопросы

  • Нет связанных вопросов^_^