线程转储中的 "Locked ownable synchronizers" 是什么?
What is "Locked ownable synchronizers" in thread dump?
我想了解线程转储中的 Locked ownable synchronizers
指的是什么?
我开始使用 ReentrantReadWriteLock
有一个处于 WAITING
状态的线程,正在等待另一个处于 WAITING
状态的线程的 "locked ownable synchronizers" 列表中的 ReentrantReadWriteLock$FairSync
(一个ThreadPoolExecutor
).
我找不到太多相关信息。它是某种锁 "passed onto" 线程吗?我试图弄清楚我的死锁是从哪里来的,我看不到任何线程主动锁定它们(即在任何堆栈跟踪中都没有相应的 - locked <0x...>
)。
An ownable synchronizer is a synchronizer that may be exclusively
owned by a thread and uses AbstractOwnableSynchronizer (or its
subclass) to implement its synchronization property. ReentrantLock and
ReentrantReadWriteLock are two examples of ownable synchronizers
provided by the platform.
正确使用ReentrantLock 并不像看起来那么容易。它有几个陷阱。如果我们谈论死锁,我认为您需要知道:
1.
The main explanation we found at this point is associated with the
usage of the ReentrantLock READ lock. The read locks are normally not
designed to have a notion of ownership. Since there is not a record of
which thread holds a read lock, this appears to prevent the HotSpot
JVM deadlock detector logic to detect deadlock involving read locks.
Some improvements were implemented since then but we can see that the
JVM still cannot detect this special deadlock scenario.
来自不错的文章“Java concurrency: the hidden thread deadlocks”
如果您可以访问源代码 getReadHoldCount() 方法可以帮助调查死锁。
2. 从 readLock 到 writeLock 的正确升级 - "Java ReentrantReadWriteLocks - how to safely acquire write lock?"
TL;DR: 写锁出现在 "ownable synchronizers" 列表中,读锁没有出现 .
我最终得到了以下 MVCE,试图了解 "ownable synchronizer" 的内容。这个想法是有两个线程 locking/unlocking read/write 可重入锁,并在不同的时间查看对不同线程转储的影响(在 jVisualVM 中,当 Eclipse 项目在特定行的断点处暂停时)。
代码如下:
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)
现在,线程转储。
当线程 "main" 获得读锁时,将获取线程转储 #1。我们可以看到,no "ownable synchronizer" 属于线程:
"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" 获得写锁后获取的。它出现在 "ownable synchronizers":
"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)
所以写锁会出现在"locked ownable synchronizers"的列表中,而读锁不会。尽管 getReadHoldCount()
显示当前线程获取的读取锁的数量,但读取 "locking" 似乎不属于特定线程,因此不在列表中。这使得调试死锁变得困难(或者说 "not as easy as with jVisualVM")。
编辑:帮助找出 copy/paste 锁被占用但未释放的错误,例如:
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
) 的详细信息。
我想了解线程转储中的 Locked ownable synchronizers
指的是什么?
我开始使用 ReentrantReadWriteLock
有一个处于 WAITING
状态的线程,正在等待另一个处于 WAITING
状态的线程的 "locked ownable synchronizers" 列表中的 ReentrantReadWriteLock$FairSync
(一个ThreadPoolExecutor
).
我找不到太多相关信息。它是某种锁 "passed onto" 线程吗?我试图弄清楚我的死锁是从哪里来的,我看不到任何线程主动锁定它们(即在任何堆栈跟踪中都没有相应的 - locked <0x...>
)。
An ownable synchronizer is a synchronizer that may be exclusively owned by a thread and uses AbstractOwnableSynchronizer (or its subclass) to implement its synchronization property. ReentrantLock and ReentrantReadWriteLock are two examples of ownable synchronizers provided by the platform.
正确使用ReentrantLock 并不像看起来那么容易。它有几个陷阱。如果我们谈论死锁,我认为您需要知道:
1.
The main explanation we found at this point is associated with the usage of the ReentrantLock READ lock. The read locks are normally not designed to have a notion of ownership. Since there is not a record of which thread holds a read lock, this appears to prevent the HotSpot JVM deadlock detector logic to detect deadlock involving read locks.
Some improvements were implemented since then but we can see that the JVM still cannot detect this special deadlock scenario.
来自不错的文章“Java concurrency: the hidden thread deadlocks”
如果您可以访问源代码 getReadHoldCount() 方法可以帮助调查死锁。
2. 从 readLock 到 writeLock 的正确升级 - "Java ReentrantReadWriteLocks - how to safely acquire write lock?"
TL;DR: 写锁出现在 "ownable synchronizers" 列表中,读锁没有出现 .
我最终得到了以下 MVCE,试图了解 "ownable synchronizer" 的内容。这个想法是有两个线程 locking/unlocking read/write 可重入锁,并在不同的时间查看对不同线程转储的影响(在 jVisualVM 中,当 Eclipse 项目在特定行的断点处暂停时)。
代码如下:
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)
现在,线程转储。
当线程 "main" 获得读锁时,将获取线程转储 #1。我们可以看到,no "ownable synchronizer" 属于线程:
"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" 获得写锁后获取的。它出现在 "ownable synchronizers":
"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)
所以写锁会出现在"locked ownable synchronizers"的列表中,而读锁不会。尽管 getReadHoldCount()
显示当前线程获取的读取锁的数量,但读取 "locking" 似乎不属于特定线程,因此不在列表中。这使得调试死锁变得困难(或者说 "not as easy as with jVisualVM")。
编辑:帮助找出 copy/paste 锁被占用但未释放的错误,例如:
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
) 的详细信息。