2016-12-23 55 views
6

Locked ownable synchronizersはスレッドダンプで何を参照しているのか理解しようとしていますか?私はReentrantReadWriteLockを使用して開始スレッドダンプの「ロックされた所有可能な同期装置」とは何ですか?

WAITING状態(ThreadPoolExecutor)内の別のスレッドの「ロックされた所有可能なシンクロナイザ」リストにReentrantReadWriteLock$FairSyncを待って、WAITING状態のスレッドを持っています。

多くの情報が見つかりませんでした。それはロックのいくつかの種類のスレッドに "渡された"ですか?私はどこからデッドロックが発生しているのか把握しようとしていて、スレッドを積極的にロックしているスレッドは見えません(つまり、スタックトレースには対応する- locked <0x...>はありません)。

答えて

4

TL; DR:書き込みロックは読み取りロックしない、 "所有可能なシンクロナイザ" リストに表示されます。

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

コードが(パッケージに "ロック")である:ここでは

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) 

、スレッドダンプ。

スレッド「main」に読み取りロックが設定されている場合、スレッドダンプ#1が取得されます。私たちが見ることができるように、ない「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 

スレッドダンプ#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" 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は、プラットフォームによって提供される所有可能なシンクロナイザ の2つの例です。

+0

そのリストのロックはスレッドによって所有されていますか?スレッドダンプの '-locked <0x...>'情報が表示されない場合、どうすれば可能ですか? – Matthieu

1

正しい使用法ReentrantLockはそれほど簡単ではないようです。それにはいくつかの落とし穴があります。我々はデッドロックについて話す場合、私はあなたが知る必要があると思う:私たちは、この時点で発見

1.

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

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

あなたがソースコードgetReadHoldCount()メソッドへのアクセス権を持っている場合は、調査のデッドロックに助けることができる素晴らしい記事「Java concurrency: the hidden thread deadlocks

からです。

2. readLockからWRITELOCKへの正しいアップグレード - "Java ReentrantReadWriteLocks - how to safely acquire write lock?"

+0

+1は、読み取りロックがスレッドによって「所有」されていないことを示します。私はロック取得後にスレッドダンプを強制することによって、それが私の問題にどのように関連するのかを調べるために、さまざまなユースケースを調べます。 – Matthieu

+0

@Matthieuようこそ!何かがコードを共有していると分かったら、それは面白いでしょう –

+0

私はちょうど答えでスレッドダンプを使っていくつかのテストをしました。つまり、読み取りロックは「所有可能な同期装置」リストには表示されません。 – Matthieu

関連する問題