【问题标题】:Java concurrency: synchronized statements/methods causing StackOverflowError exceptionJava 并发:同步语句/方法导致 StackOverflowError 异常
【发布时间】:2013-05-06 20:08:18
【问题描述】:

我正在对 Java 中的事件回调处理框架进行压力测试,该框架在可能的情况下大量使用并发,因此在可能出现数据竞争时使用同步方法和语句。在一个名为 testDispatchWorstCase 的单元测试中,将同时启动 100 到 500 个线程,每个线程都使用同步语句调用方法,这些语句利用单个控制器对象的监视器,称为 MasterSemaphore。使用 100 个线程进行测试时,有时一切正常,有时我在一个或多个同步语句和/或方法中遇到 stackoverflow 异常。在使用 500 个线程进行测试时,我几乎总是在一个或多个同步语句和/或方法中遇到 stackoverflow 异常。

MasterSemaphore 提供了用于锁定同步语句的监视器,还提供了一些事件处理方法,这些方法被调用来协调各个线程对资源的访问。鉴于此,我想也许可以使用 MasterSemaphore 的成员对象,它没有其他职责,因为监视器提供者会解决问题——不幸的是,这只会导致死锁;数百个线程在 Eclipse 的调试器线程视图中停留在“监视器”状态并永远保持这种状态。 我的问题如下:

  1. 对象的监视器如何存储和跟踪对受此监视器锁定的线程的引用?

  2. 为什么我在为监视器提供程序使用 MasterSemaphore 的成员对象时遇到了死锁?我需要在 synchronizes 语句中显式调用 wait...notifyAll 吗?

  3. 当数百个线程受制于一个对象的监视器锁时,什么可能导致同步语句中的堆栈溢出?

堆栈跟踪是:

01-02 07:47:44.910 27698 27842 E AndroidRuntime: FATAL EXCEPTION: HPCHead Spawned 
Thread #24

01-02 07:47:44.910 27698 27842 E AndroidRuntime: java.lang.StackOverflowError

01-02 07:47:44.910 27698 27842 E AndroidRuntime:    at   
java.lang.AbstractStringBuilder.append0(AbstractStringBuilder.java:147)

01-02 07:47:44.910 27698 27842 E AndroidRuntime:    at 
java.lang.IntegralToString.convertInt(IntegralToString.java:209)

01-02 07:47:44.910 27698 27842 E AndroidRuntime:    at 
java.lang.IntegralToString.appendInt(IntegralToString.java:173)

01-02 07:47:44.910 27698 27842 E AndroidRuntime:    at 
java.lang.StringBuilder.append(StringBuilder.java:139)

01-02 07:47:44.910 27698 27842 E AndroidRuntime:    at 
java.lang.Thread.toString(Thread.java:1098)

01-02 07:47:44.910 27698 27842 E AndroidRuntime:    at 
com.hdradio.hdradiomanagerjc.hpc.MasterSemaphore.
addRegistrantForCallback_PrefabEventHandling(MasterSemaphore.java:2524)

01-02 07:47:44.910 27698 27842 E AndroidRuntime:    at 
com.hdradio.hdradiomanagerjc.hpc.MasterEventHandler$EventIDFreedHandler.
onUpdateNeeded(MasterEventHandler.java:221)

01-02 07:47:44.910 27698 27842 E AndroidRuntime:    at     
com.hdradio.hdradiomanagerjc.hpc.CallbackDataRecord.
postToQueueHead(CallbackDataRecord.java:107)

01-02 07:47:44.910 27698 27842 E AndroidRuntime:    at 
com.hdradio.hdradiomanagerjc.hpc.MasterSemaphore.
onFinishedEventProcessing(MasterSemaphore.java:1219)

01-02 07:47:44.910 27698 27842 E AndroidRuntime:    at 
com.hdradio.hdradiomanagerjc.hpc.MasterSemaphore.
addRegistrantForCallback_PrefabEventHandling(MasterSemaphore.java:2597)

01-02 07:47:44.910 27698 27842 E AndroidRuntime:    at 
com.hdradio.hdradiomanagerjc.hpc.MasterEventHandler$EventIDFreedHandler.
onUpdateNeeded(MasterEventHandler.java:221)

01-02 07:47:44.910 27698 27842 E AndroidRuntime:    at 
com.hdradio.hdradiomanagerjc.hpc.CallbackDataRecord.
postToQueueHead(CallbackDataRecord.java:107)

01-02 07:47:44.910 27698 27842 E AndroidRuntime:    at 
com.hdradio.hdradiomanagerjc.hpc.MasterSemaphore.
onFinishedEventProcessing(MasterSemaphore.java:1219)

01-02 07:47:44.910 27698 27842 E AndroidRuntime:    at 
com.hdradio.hdradiomanagerjc.hpc.MasterSemaphore.
addRegistrantForCallback_PrefabEventHandling(MasterSemaphore.java:2597)

01-02 07:47:44.910 27698 27842 E AndroidRuntime:    at 
com.hdradio.hdradiomanagerjc.hpc.MasterEventHandler$EventIDFreedHandler
.onUpdateNeeded(MasterEventHandler.java:221)

01-02 07:47:44.910 27698 27842 E AndroidRuntime:    at 
com.hdradio.hdradiomanagerjc.hpc.CallbackDataRecord.
postToQueueHead(CallbackDataRecord.java:107)

01-02 07:47:44.910 27698 27842 E AndroidRuntime:    at 
com.hdradio.hdradiomanagerjc.hpc.MasterSemaphore.
onFinishedEventProcessing(MasterSemaphore.java:1219)

01-02 07:47:44.910 27698 27842 E AndroidRuntime:    at 
com.hdradio.hdradiomanagerjc.hpc.MasterSemaphore.
addRegistrantForCallback_PrefabEventHandling(MasterSemaphore.java:2597)

01-02 07:47:44.910 27698 27842 E AndroidRuntime:    at 
com.hdradio.hdradiomanagerjc.hpc.MasterEventHandler$EventIDFreedHandler.
onUpdateNeeded(MasterEventHandler.java:221)

01-02 07:47:44.910 27698 27842 E AndroidRuntime:    at 
com.hdradio.hdradiomanagerjc.hpc.CallbackDataRecord.
postToQueueHead(CallbackDataRecord.java:107)

01-02 07:47:44.910 27698 27842 E AndroidRuntime:    at 
com.hdradio.hdradiomanagerjc.hpc.MasterSemaphore.
onFinishedEventProcessing(MasterSemaphore.java:1219)

01-02 07:47:44.910 27698 27842 E AndroidRuntime:    at 
com.hdradio.hdradiomanagerjc.hpc.MasterSemaphore.
addRegistrantForCallback_PrefabEventHandling(MasterSemaphore.java:2597)

01-02 07:47:44.910 27698 27842 E AndroidRuntime:    at 
com.hdradio.hdradiomanagerjc.hpc.MasterEventHandler$EventIDFreedHandler.
onUpdateNeeded(MasterEventHandler.java:221)

01-02 07:47:44.910 27698 27842 E AndroidRuntime:    at 
com.hdradio.hdradiomanagerjc.hpc.CallbackDataRecord.
postToQueueHead(CallbackDataRecord.java:107)

01-02 07:47:44.910 27698 27842 E AndroidRuntime:    at 
com.hdradio.hdradiomanagerjc.hpc.MasterSemaphore.
onFinishedEventProcessing(MasterSemaphore.java:1219)

01-02 07:47:44.910 27698 27842 E AndroidRuntime:    at 
com.hdradio.hdradiomanagerjc.hpc.MasterSemaphore.
addRegistrantForCallback_PrefabEventHandling(MasterSemaphore.java:2597)

01-02 07:47:44.910 27698 27842 E AndroidRuntime:    at 
com.hdradio.hdradiomanagerjc.hpc.MasterEventHandler$EventIDFreedHandler.
onUpdateNeeded(MasterEventHandler.java:221)

01-02 07:47:44.910 27698 27842 E AndroidRuntime:    at 
com.hdradio.hdradiomanagerjc.hpc.CallbackDataRecord.
postToQueueHead(CallbackDataRecord.java:107)

01-02 07:47:44.910 27698 27842 E AndroidRuntime:    at 
com.hdradio.hdradiomanagerjc.hpc.MasterSemaphore.
onFinishedEventProcessing(MasterSemaphore.java:1219)

01-02 07:47:44.910 27698 27842 E AndroidRuntime:    at 
com.hdradio.hdradiomanagerjc.hpc.MasterSemaphore.
addRegistrantForCallback_PrefabEventHandling(MasterSemaphore.java:2597)

01-02 07:47:44.910 27698 27842 E AndroidRuntime:    at 
com.hdradio.hdradiomanagerjc.hpc.MasterEventHandler$EventIDFreedHandler.
onUpdateNeeded(MasterEventHandler.java:221)

01-02 07:47:44.910 27698 27842 E AndroidRuntime:    at 
com.hdradio.hdradiomanagerjc.hpc.CallbackDataRecord.
postToQueueHead(CallbackDataRecord.java:107)

01-02 07:47:44.910 27698 27842 E AndroidRuntime:    at 
com.hdradio.hdradiomanagerjc.hpc.MasterSemaphore.
onFinishedEventProcessing(MasterSemaphore.java:1219)

01-02 07:47:44.910 27698 27842 E AndroidRuntime:    at 
com.hdradio.hdradiomanagerjc.hpc.MasterSemaphore.
addRegistrantForCallback_PrefabEventHandling(MasterSemaphore.java:2597)

01-02 07:47:44.910 27698 27842 E AndroidRuntime:    at 
com.hdradio.hdradiomanagerjc.hpc.MasterEventHandler$EventIDFreedHandler.
onUpdateNeeded(MasterEventHandler.java:221)

01-02 07:47:44.910 27698 27842 E AndroidRuntime:    at 
com.hdradio.hdradiomanagerjc.hpc.CallbackDataRecord.
postToQueueHead(CallbackDataRecord.java:107)

01-02 07:47:44.910 27698 27842 E AndroidRuntime:    at 
com.hdradio.hdradiomanagerjc.hpc.MasterSemaphore.
onFinishedEventProcessing(MasterSemaphore.java:1219)

【问题讨论】:

  • 你能发布 StackOverflowException 的堆栈跟踪吗?
  • StackOverflow 异常应该与同步问题无关。听起来完全不同的问题。同步只会导致线程阻塞和方法串行执行而不是并行执行。您根本不需要使用等待/通知。
  • 我不希望堆栈溢出来自同步相关问题。这可能是一个不同的错误。我看到的唯一例外是您在 Android 上执行此操作。那里的 JavaVM 是否根据线程数调整堆栈大小? Java 通常为每个线程提供 512k 的堆栈(注意:很大程度上取决于许多因素),但也许 Android 具有适应性并给您提供很小、很小、很小的堆栈?
  • @DavidWasser 这是我的想法,我希望这可能是这种情况......我只是想我会把问题放在那里,以防我错过了关于 Java 内存模型的一些重要内容.我还编辑了问题以包含堆栈跟踪
  • 堆栈跟踪在哪里?

标签: java android concurrency stack-overflow


【解决方案1】:

回答您的问题:

  1. 这是article that describes how synchronization using object monitors works

  2. 我不确定您为什么会陷入僵局。我需要查看更多代码才能提供帮助。不过,正如我在之前的评论中所说,您不需要使用 wait'/notifyAllwhen usingsynchronized. The JVM handles all the locking and waiting automatically for you. Of course, this doesn't mean that you can't program yourself into a deadlock situation (that's actually pretty easy to do). Addingnotify() 调用虽然不会解除死锁。

  3. 所以堆栈溢出问题来自递归调用堆栈,如下所示:

    ...

    MasterSemaphore.onFinishedEventProcessing(MasterSemaphore.java:1219) MasterSemaphore.addRegistrantForCallback_PrefabEventHandling(MasterSemaphore.java:2597) MasterEventHandler$EventIDFreedHandler.onUpdateNeeded(MasterEventHandler.java:221) CallbackDataRecord.postToQueueHead(CallbackDataRecord.java:107) MasterSemaphore.onFinishedEventProcessing(MasterSemaphore.java:1219)

所以onFinishedEventProcessing() 被递归调用,这导致堆栈溢出。您应该看看为什么会发生这种情况(如果需要,添加一些日志记录)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-11-21
    • 1970-01-01
    • 2011-05-16
    • 2022-12-10
    • 2012-02-03
    • 1970-01-01
    • 2010-12-15
    相关资源
    最近更新 更多