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
)を表示します。
いい仕事です!共にありがとう –
誰かがもっと徹底した説明をしてくれたら、まだ私の答えは受け入れません。 – Matthieu
素敵な分析。要約/結論を最初または最後にハイライトします。 –