【问题标题】:What is "Locked ownable synchronizers" in thread dump?什么是线程转储中的“锁定的可拥有同步器”?
【发布时间】:2016-12-23 11:23:19
【问题描述】:

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

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

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

【问题讨论】:

    标签: java multithreading thread-dump


    【解决方案1】:

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

    我最终使用了以下 MVCE 来尝试了解“可拥有的同步器”的含义。这个想法是让两个线程锁定/解锁读/写重入锁,并查看在不同时间对不同线程转储的影响(在 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)
    

    现在,线程转储。

    线程“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
    

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

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

    编辑:帮助找出锁被占用和未释放的复制/粘贴错误,例如:

    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) 的详细信息。

    【讨论】:

    • 干得好!感谢分享
    • 我不接受我的回答,以防有人提出更详尽的解释。
    • 精彩的分析。在开头或结尾突出摘要/结论。
    【解决方案2】:

    来自Java 7 documentation:

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

    【讨论】:

    • 那么该列表中的锁归线程所有?如果我在线程转储中没有看到任何- locked &lt;0x...&gt; 信息,这怎么可能?
    【解决方案3】:

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

    1.

    我们在这一点上找到的主要解释与 ReentrantLock READ 锁的使用。读锁通常不会 旨在具有所有权的概念。由于没有记录 哪个线程持有读锁,这似乎可以防止 HotSpot JVM 死锁检测器逻辑,用于检测涉及读锁的死锁。

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

    来自好文章“Java concurrency: the hidden thread deadlocks”

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

    2. 正确从 readLock 升级到 writeLock - "Java ReentrantReadWriteLocks - how to safely acquire write lock?"

    【讨论】:

    • +1 用于指出读锁没有被线程标记为“拥有”。我将研究不同的用例,通过在获取锁后强制线程转储来了解它与我的问题的关系。
    • @Matthieu 不客气!如果你发现了什么,请分享一些代码,这会很有趣
    • 我刚刚在答案中对线程转储进行了一些测试。简而言之:读锁不会出现在“可拥有的同步器”列表中。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-09-13
    • 1970-01-01
    • 2011-10-12
    • 2010-09-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多