我想了解Locked ownable synchronizers
在线程转储中引用了什么?
我开始使用ReentrantReadWriteLock
有WAITING
状态的线程,等待"锁定的可拥有同步器中的ReentrantReadWriteLock$FairSync
" WAITING
州中另一个线程的列表(ThreadPoolExecutor
)。
我无法找到有关它的更多信息。它是某种锁和#34;传递给&#34;线程?我试图弄清楚我的死锁来自哪里,我无法看到任何线程主动锁定那些(即任何堆栈跟踪中没有相应的- locked <0x...>
)。
答案 0 :(得分:9)
TL; DR:写锁定出现在&#34;拥有的同步器中#34;列表,读锁不。
我最终得到了以下MVCE来尝试理解&#34;拥有同步器&#34;的内容。我们的想法是让两个线程锁定/解锁读/写可重入锁,并在不同的时间看到对不同线程转储的影响(在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是在线程&#34; main&#34;得到了一个读锁定。我们可以看到,没有&#34;拥有同步器&#34;由线程:
拥有"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在线程&#34;其他&#34;之后进行。已经采取了写锁定。它出现在&#34;拥有的同步器&#34;:
中"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在线程&#34;其他&#34;之后进行。已经释放了写锁(并且已经死了),并且线程&#34;主要&#34;采取了它:
"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)
因此,当锁定读取锁定时,写锁定将出现在锁定的可拥有同步器列表中#34;即使getReadHoldCount()
显示当前线程占用的读锁数,也可以读取&#34;锁定&#34;似乎不属于特定的线程,因此不在列表中。这使得调试死锁变得困难(或者说#34;不像使用jVisualVM&#34那么容易;)。
编辑:帮助确定使用锁定而未发布的锁定的复制/粘贴错误,例如:
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
)的详细信息。
答案 1 :(得分:7)
可拥有的同步器是一个可以独占的同步器 由一个线程拥有并使用AbstractOwnableSynchronizer(或它的 子类)实现其同步属性。 ReentrantLock和 ReentrantReadWriteLock是可拥有同步器的两个示例 由平台提供。
答案 2 :(得分:1)
正确使用ReentrantLock它看起来并不那么容易。它有几个陷阱。如果我们谈论死锁,我想你需要知道:
<强> 1 强>
我们在这一点上找到的主要解释与 使用ReentrantLock READ锁。读锁通常不是 旨在拥有所有权的概念。由于没有记录 哪个线程持有读锁定,这似乎阻止了HotSpot JVM死锁检测器逻辑,用于检测涉及读锁的死锁。
从那时起实施了一些改进,但我们可以看到 JVM仍然无法检测到这种特殊的死锁情况。
来自好文章&#34; Java concurrency: the hidden thread deadlocks&#34;
如果您有权访问源代码getReadHoldCount(),则可以帮助调查死锁。
2. :从readLock正确升级到writeLock - "Java ReentrantReadWriteLocks - how to safely acquire write lock?"