java 线程转储中的“锁定的可拥有同步器”是什么?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/41300520/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 05:48:06  来源:igfitidea点击:

What is "Locked ownable synchronizers" in thread dump?

javamultithreadingthread-dump

提问by Matthieu

I am trying to understand what does Locked ownable synchronizersrefer to in a thread dump?

我想了解Locked ownable synchronizers线程转储中指的是什么?

I started using ReentrantReadWriteLockhave a thread in WAITINGstate, waiting for a ReentrantReadWriteLock$FairSyncin the "locked ownable synchronizers" list of another thread in WAITINGstate (a ThreadPoolExecutor).

我开始使用ReentrantReadWriteLock处于WAITING状态的线程,等待ReentrantReadWriteLock$FairSync处于WAITING状态 (a ThreadPoolExecutor)的另一个线程的“锁定的可拥有同步器”列表中的a 。

I couldn't find much information about that. Is it some kind of locks "passed onto" the thread? I'm trying to figure out where my deadlock comes from and I can't see any thread actively locking those (i.e. no corresponding - locked <0x...>in any stack trace).

我找不到太多相关信息。它是某种“传递到”线程的锁吗?我试图弄清楚我的死锁来自哪里,我看不到任何线程主动锁定它们(即- locked <0x...>在任何堆栈跟踪中都没有对应)。

采纳答案by Matthieu

TL;DR: write locks appear in the "ownable synchronizers" list, read locks don't.

TL;DR:写锁出现在“拥有的同步器”列表中,读锁没有

I ended up with the following MVCE to try and understand what's with "ownable synchronizer". The idea was to have two threads locking/unlocking read/write reentrant locks and see the effect on different thread dumps at different timings (taken in jVisualVM while the Eclipse project was paused in breakpoints at specific lines).

我最终使用了以下 MVCE 来尝试了解“可拥有的同步器”的含义。这个想法是让两个线程锁定/解锁读/写可重入锁,并在不同的时间查看对不同线程转储的影响(在 jVisualVM 中采用,而 Eclipse 项目在特定行的断点处暂停)。

Here is the code:

这是代码:

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();
        }
    }
}

Here is the output:

这是输出:

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)

Now, thread dumps.

现在,线程转储。

Thread dump #1 is taken when thread "main" got a read lock. As we can see, no "ownable synchronizer" is owned by the thread:

当线程“main”获得读锁时,线程转储 #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

Thread dump #2 is taken after thread "other" has taken the write lock. It appears in the "ownable synchronizers":

线程转储#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)

Thread dump #3 is taken after thread "other" has released the write lock (and died), and thread "main" has taken it:

线程转储 #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)

So write locks will appear in the list of "locked ownable synchronizers", when read locks won't. Even though getReadHoldCount()shows the number of read locks taken by the current thread, a read "locking" doesn't seem to belong to a particular thread and is therefore absent from the list. And that makes it difficult to debug deadlocks (or let's say "not as easy as with jVisualVM").

因此,当读锁不会时,写锁将出现在“锁定的可拥有同步器”列表中。尽管getReadHoldCount()显示了当前线程占用的读锁数量,但读“锁定”似乎不属于特定线程,因此不在列表中。这使得调试死锁变得困难(或者说“不像使用 jVisualVM 那样容易”)。

EDIT: To help figuring out copy/paste errors with locks taken and not released such as in:

编辑:为了帮助找出锁定而不释放的复制/粘贴错误,例如:

myLock.readLock().lock();
try {
    // ...
} finally {
    myLock.readLock().lock(); // Oops! Should be "unlock()"
}

you can use the following Linux command line at the root of your source directory:

您可以在源目录的根目录下使用以下 Linux 命令行:

find . -name '*.java' -exec grep -Hn 'myLock.readLock().lock();' {} \; | wc -l

will display how many read locks are taken, and:

将显示读取锁多少和:

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

will display how many read locks are released. If numbers don't match, remove the | wc -lto show the details of file names (grep -H) and line number (grep -n).

将显示释放了多少读锁。如果数字不匹配,请删除| wc -l以显示文件名 ( grep -H) 和行号 ( grep -n)的详细信息。

回答by AR1

From Java 7 documentation:

来自Java 7 文档

An ownable synchronizer is a synchronizer that may be exclusively owned by a threadand uses AbstractOwnableSynchronizer (or its subclass) to implement its synchronization property. ReentrantLock and ReentrantReadWriteLock are two examples of ownable synchronizers provided by the platform.

一个可拥有的同步器是一个可能被一个线程独占拥有并使用 AbstractOwnableSynchronizer(或其子类)来实现其同步属性的同步器。ReentrantLock 和 ReentrantReadWriteLock 是平台提供的可拥有同步器的两个示例。

回答by Vladislav Kysliy

Correct use ReentrantLock it isn't so easy as it seems. It has several pitfalls. If we speak about deadlocks i think you need to know :

正确使用 ReentrantLock 并不像看起来那么容易。它有几个陷阱。如果我们谈论死锁,我认为您需要知道:

1.

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.

我们此时发现的主要解释与 ReentrantLock READ 锁的使用有关。读锁通常没有设计成拥有所有权的概念。由于没有记录哪个线程持有读锁,这似乎会阻止 HotSpot JVM 死锁检测器逻辑检测涉及读锁的死锁。

从那时起实施了一些改进,但我们可以看到 JVM 仍然无法检测到这种特殊的死锁情况。

It's from nice article "Java concurrency: the hidden thread deadlocks"

它来自不错的文章“ Java 并发:隐藏的线程死锁

If you have access to source code getReadHoldCount()method can help in investigation deadlocks.

如果您有权访问源代码,则getReadHoldCount()方法可以帮助调查死锁。

2.Correct upgrade from readLock to writeLock - "Java ReentrantReadWriteLocks - how to safely acquire write lock?"

2.从readLock到writeLock的正确升级—— 《Java ReentrantReadWriteLocks——如何安全地获取写锁?》