Что такое "заблокированные собственные синхронизаторы" в дампе потоков?

Я пытаюсь понять, что значит Locked ownable synchronizers см. в дампе потока?

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

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

3 ответов


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

я закончил со следующим 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" получил блокировку чтения. Как видим, " собственный синхронизатор " не принадлежит потоку:

"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

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

"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)

дамп потока #3 берется после того, как поток "другой" имеет отпустил замок записи (и умер), а нить "main" взяла его:

"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").

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

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


с документация Java 7:

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


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

1.

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

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

Это из хорошей статьи"параллелизм Java: скрытые блокировки потока"

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

2. Правильное обновление с readLock до writeLock -"Java ReentrantReadWriteLocks-как безопасно получить блокировку записи?"