同步部分不会阻塞!

发布于 2024-09-09 09:15:40 字数 2079 浏览 4 评论 0原文

昨天我注意到一些非常奇怪的事情。似乎两个线程同时进入锁定同一个对象的两个同步块。

包含相关代码的类 (MyClass) 看起来与此类似:

private static int[]    myLock  = new int[0];

protected static int methodA(final long handle, final byte[] sort) {
    synchronized (myLock) {
        return xsMethodA(handle, sort);
    }
}

protected static int methodB(final long handle) {
    synchronized (myLock) {
        return xsMethodB(handle);
    }
}

我创建了运行上述类的应用程序的线程转储,当我看到这一点时感到非常惊讶:(

"http-8080-136" daemon prio=10 tid=0x00000000447df000 nid=0x70ed waiting for monitor entry [0x00007fd862aea000]
   java.lang.Thread.State: BLOCKED (on object monitor)
    at com.MyClass.methodA(MyClass.java:750)
    - locked <0x00007fd8a6b8c790> (a [I)
    at com.SomeOtherClass.otherMethod(SomeOtherClass.java:226)
    ...

"http-8080-111" daemon prio=10 tid=0x00007fd87d1a0000 nid=0x70c8 waiting for monitor entry [0x00007fd86e15f000]
   java.lang.Thread.State: BLOCKED (on object monitor)
    at com.MyClass.methodB(MyClass.java:991)
    - locked <0x00007fd8a6b8c790> (a [I)
    at com.SomeOtherClass.yetAnotherMethod(SomeOtherClass.java:3231)
    ...

我更改了类和方法名称为了简单起见,所以不要被愚蠢的名称所迷惑。)

线程 http-8080-136 和 http-8080-111 似乎都获得了 myLock 上的锁。它是同一个对象,因为对象地址相同:0x00007fd8a6b8c790。 Java 运行时规范对于 synchronized 关键字是这样说的:

同步语句代表执行线程获取互斥锁(第 17.1 节),执行一个块,然后释放该锁。当执行线程拥有锁时,没有其他线程可以获得该锁。 [Java 语言规范,14.19]< /p>

< strong>那么这怎么可能呢?

线程转储中还有另外 44 个线程“等待”锁定。这是线程正在等待时的样子:

"http-8080-146" daemon prio=10 tid=0x00007fd786dab000 nid=0x184b waiting for monitor entry [0x00007fd8393b6000]
   java.lang.Thread.State: BLOCKED (on object monitor)
    at com.MyClass.methodC(MyClass.java:750)
    - waiting to lock <0x00007fd8a6b8c790> (a [I)
    at com.SomeOtherClass.yetAnoterMethod2(SomeOtherClass.java:226)

I've noticed something very strange yesterday. It seems that two threads are entering two synchronized blocks locking on the same object at the same time.

The class (MyClass) containing the relevant code looks similar to this:

private static int[]    myLock  = new int[0];

protected static int methodA(final long handle, final byte[] sort) {
    synchronized (myLock) {
        return xsMethodA(handle, sort);
    }
}

protected static int methodB(final long handle) {
    synchronized (myLock) {
        return xsMethodB(handle);
    }
}

I created a thread dump of my application running the above class and was very surprised as I saw this:

"http-8080-136" daemon prio=10 tid=0x00000000447df000 nid=0x70ed waiting for monitor entry [0x00007fd862aea000]
   java.lang.Thread.State: BLOCKED (on object monitor)
    at com.MyClass.methodA(MyClass.java:750)
    - locked <0x00007fd8a6b8c790> (a [I)
    at com.SomeOtherClass.otherMethod(SomeOtherClass.java:226)
    ...

"http-8080-111" daemon prio=10 tid=0x00007fd87d1a0000 nid=0x70c8 waiting for monitor entry [0x00007fd86e15f000]
   java.lang.Thread.State: BLOCKED (on object monitor)
    at com.MyClass.methodB(MyClass.java:991)
    - locked <0x00007fd8a6b8c790> (a [I)
    at com.SomeOtherClass.yetAnotherMethod(SomeOtherClass.java:3231)
    ...

(I changed the class and method names for the case of simplicity, so don't get confused by the silly names.)

It seems that thread http-8080-136 and http-8080-111 have both acquired the lock on myLock. It is the same object as the object address is the same: 0x00007fd8a6b8c790. The Java Runtime Specification says this about the synchronized keyword:

A synchronized statement acquires a mutual-exclusion lock (§17.1) on behalf of the executing thread, executes a block, then releases the lock. While the executing thread owns the lock, no other thread may acquire the lock. [The Java Language Specification, 14.19]

So how is this even possible?

There are another 44 threads in the thread dump "waiting" for the lock. This is how it looks like if a thread is waiting:

"http-8080-146" daemon prio=10 tid=0x00007fd786dab000 nid=0x184b waiting for monitor entry [0x00007fd8393b6000]
   java.lang.Thread.State: BLOCKED (on object monitor)
    at com.MyClass.methodC(MyClass.java:750)
    - waiting to lock <0x00007fd8a6b8c790> (a [I)
    at com.SomeOtherClass.yetAnoterMethod2(SomeOtherClass.java:226)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

知你几分 2024-09-16 09:15:40

我在热点开发邮件列表上问了同样的问题,并收到了 Christopher Phillips 的非常好的答案:


嗨,Eduard,

我认为线程转储具有误导性。

如果您确实认为两者同时处于锁定状态,您可能应该获得一个 gcore(外部一致)。

您看到的“等待监视器条目”状态实际上是 MONITOR_WAIT ,它可以表示实际获取热锁之前的以下代码:
(另请参阅 osThread.hpp 中的 OSThreadContendState)从以下位置调用:
src/share/vm/runtime/synchronizer.cpp

3413      OSThreadContendState osts(Self->osthread());
3414      ThreadBlockInVM tbivm(jt);
3415
3416      Self->set_current_pending_monitor(this);
3417
3418      // TODO-FIXME: change the following for(;;) loop to straight-line code.
3419      for (;;) {
3420        jt->set_suspend_equivalent();
3421        // cleared by handle_special_suspend_equivalent_condition()
3422        // or java_suspend_self()
3423
3424        EnterI (THREAD) ;
3425
3426        if (!ExitSuspendEquivalent(jt)) break ;
3427
3428        //
3429        // We have acquired the contended monitor, but while we were
3430        // waiting another thread suspended us. We don't want to enter
3431        // the monitor while suspended because that would surprise the
3432        // thread that suspended us.

克里斯

I've asked the same question on the hotspot-dev mailing list and received a very goot answer from Christopher Phillips:


Hi Eduard

I think its the thread dump that is misleading.

If you really think that the 2 are in the lock simultaneously you should probably get a gcore (which is externally consistent).

The state you see "waiting for monitor entry" is actually MONITOR_WAIT which can represent the following code before actual acquisition of a hot lock :
(also see OSThreadContendState in osThread.hpp) called from:
src/share/vm/runtime/synchronizer.cpp

3413      OSThreadContendState osts(Self->osthread());
3414      ThreadBlockInVM tbivm(jt);
3415
3416      Self->set_current_pending_monitor(this);
3417
3418      // TODO-FIXME: change the following for(;;) loop to straight-line code.
3419      for (;;) {
3420        jt->set_suspend_equivalent();
3421        // cleared by handle_special_suspend_equivalent_condition()
3422        // or java_suspend_self()
3423
3424        EnterI (THREAD) ;
3425
3426        if (!ExitSuspendEquivalent(jt)) break ;
3427
3428        //
3429        // We have acquired the contended monitor, but while we were
3430        // waiting another thread suspended us. We don't want to enter
3431        // the monitor while suspended because that would surprise the
3432        // thread that suspended us.

Chris

天煞孤星 2024-09-16 09:15:40

线程转储是如何获取的?如果线程没有暂停,锁所有权可能会在转储一个线程和下一个线程之间发生变化。

How was the thread dump taken? If the threads were not paused the lock ownership could have changed between dumping one thread and the next.

夏末 2024-09-16 09:15:40

我认为相关信息是:“等待监视器条目”,这对于两个线程都是相同的。由于两个线程(在线程转储中)都被标记为守护线程,我猜想还必须有一个主线程同时运行。主线程是否有可能是阻塞其他两个线程的当前监视器所有者?

I think the relevant information is: "waiting for monitor entry", which is the same for both threads. Since both threads (in the thread dump) are marked deamon threads, I guess there must also be a main-thread running at the same time. Is it possible that the main-thread is the current monitor owner who blocks the other two threads?

吖咩 2024-09-16 09:15:40

它们尚未获取锁,否则您会在堆栈跟踪中看到 xsMethodA 或 xsMethodB。

They haven't acquired lock, otherwise you would see xsMethodA or xsMethodB in a stacktrace.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文