web-dev-qa-db-ja.com

スレッドダンプの「ロックされた所有可能なシンクロナイザー」とは何ですか?

私はLocked ownable synchronizersスレッドダンプで参照しますか?

ReentrantReadWriteLock を使用し始め、スレッドをWAITING状態にして、ReentrantReadWriteLock$FairSyncは、WAITING状態(a ThreadPoolExecutor)の別のスレッドの「ロックされた所有可能なシンクロナイザー」リストにあります。

私はそれについて多くの情報を見つけることができませんでした。スレッドに「渡される」ロックのようなものですか?私はデッドロックがどこから来たのかを把握しようとしていますが、それらをアクティブにロックしているスレッドを見ることができません(つまり、対応する- locked <0x...>任意のスタックトレース)。

19
Matthieu

TL; DR:「所有可能なシンクロナイザー」リストに書き込みロックが表示される読み取りロックはしない

「所有可能なシンクロナイザー」とは何かを試して理解するために、次のMVCEを見つけました。アイデアは、2つのスレッドが読み取り/書き込みリエントラントロックをロック/ロック解除し、異なるタイミングで異なるスレッドダンプに及ぼす影響を確認することでした(Eclipseプロジェクトが特定の行のブレークポイントで一時停止している間にjVisualVMで取得)。

コードは次のとおりです。

_package lock;

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" 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は、スレッド "other"が書き込みロックを取得した後に取得されます。 「所有可能なシンクロナイザー」に表示されます。

_"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は、スレッド "other"が書き込みロックを解放(および停止)し、スレッド "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
_

読み取りロックの数を表示しますtaken、および:

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

読み取りロックの数が表示されますreleased。番号が一致しない場合は、_| wc -l_を削除して、ファイル名(_grep -H_)と行番号(_grep -n_)の詳細を表示します。

10
Matthieu

Java 7ドキュメント から:

所有可能なシンクロナイザーは、スレッドのみが所有できるシンクロナイザーであり、AbstractOwnableSynchronizer(またはそのサブクラス)を使用して同期プロパティを実装します。 ReentrantLockとReentrantReadWriteLockは、プラットフォームが提供する所有可能なシンクロナイザーの2つの例です。

7
AR1

ReentrantLockを正しく使用すると、見た目ほど簡単ではありません。いくつかの落とし穴があります。デッドロックについて話す場合、あなたが知る必要があると思います:

1。

この時点で見つかった主な説明は、ReentrantLock READロックの使用に関連しています。通常、読み取りロックは所有権の概念を持つようには設計されていません。読み取りロックを保持しているスレッドのレコードがないため、これにより、HotSpot JVMデッドロック検出ロジックが読み取りロックに関連するデッドロックを検出できなくなります。

その後、いくつかの改善が実装されましたが、JVMがこの特別なデッドロックシナリオをまだ検出できないことがわかります。

これは、ニースの記事「 Java同時実行性:隠されたスレッドのデッドロック

ソースコードにアクセスできる場合 getReadHoldCount() メソッドは調査のデッドロックに役立ちます。

2。readLockからwriteLockへの正しいアップグレード- "Java ReentrantReadWriteLocks-書き込みロックを安全に取得する方法?"

1