我正在为 Gameboy 系统制作一个模拟器,并且我使用 Swing 作为我的 GUI。我的实现使用 aSwingWorker
在后台线程中运行 CPU 仿真。通过SwingWorker.doInBackground
方法,我定期发布一个包含帧缓冲区(图像数据)的数组(每当 Gameboy 模拟 VBlank 屏幕刷新时),这应该每秒发生约 60 次。我Thread.sleep(...)
在这个后台线程中调用以将 Gameboys 时间与系统时间同步。在process
方法中,我将此数据传递给 aJPanel
并调用它的repaint
方法。我的doInBackground
方法基本上是一个无限循环,模拟的 Gameboy CPU 在后台不断执行指令。
我面临的问题是在某些 ROM 上,我的应用程序只是冻结并且我的JPanel
组件没有显示屏幕更新。我尝试println
在我的后台运行线程上使用 's 输出模拟 CPU 正在执行的指令,这也卡住了,一段时间后没有生成输出。
对于某些 ROM,我根本看不到这个问题发生。
这是我的 SwingWorker 代码示例:
package com.sankar.gbemu.swing;
import com.sankar.gbemu.cpu.CPU;
import com.sankar.gbemu.gpu.LCDController;
import com.sankar.gbemu.gpu.VBlankListener;
import java.util.List;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
public class EmulationWorker extends SwingWorker<Void,int[][]> {
private CPU cpu;
private LCDController lcd;
private GBPanel panel;
private GBRenderer renderer;
public EmulationWorker(CPU cpu, LCDController lcd, GBPanel panel, GBRenderer renderer) {
this.cpu = cpu;
this.lcd = lcd;
this.panel = panel;
this.renderer = renderer;
init();
}
private void init() {
lcd.registerVBlankListener(new VBlankListener() {
private long lastRefresh = System.nanoTime();
private static final long REFRESH_INTERVAL = (long)1e9/60;
@Override
public void vBlankOccured(int[][] screenData) {
publish(screenData);
long currentTime = System.nanoTime();
long delta = currentTime - lastRefresh;
lastRefresh = currentTime;
if (delta < REFRESH_INTERVAL) {
try {
long sleepInterval = (long)((REFRESH_INTERVAL - delta)/1e6);
Thread.sleep(sleepInterval);
} catch(InterruptedException e) {
// Ignore
}
}
}
});
}
@Override
protected Void doInBackground() throws Exception {
while(true) cpu.step();
}
@Override
protected void process(List<int[][]> updates) {
if (!SwingUtilities.isEventDispatchThread()) throw new RuntimeException("Not in event dispatch thread");
renderer.updateScreenData(updates.get(updates.size() - 1));
panel.repaint();
}
}
我进行了 jstack 跟踪,发现我的 swing-worker-thread 卡在 Unsafe.park:
2013-10-10 22:17:18
Full thread dump Java HotSpot(TM) 64-Bit Server VM (24.0-b56 mixed mode):
"Attach Listener" daemon prio=5 tid=0x00007fae12049000 nid=0xc20f waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"TimerQueue" daemon prio=5 tid=0x00007fae1240b800 nid=0xbf03 waiting on condition [0x000000015a085000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x000000013ac88b60> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2043)
at java.util.concurrent.DelayQueue.take(DelayQueue.java:209)
at javax.swing.TimerQueue.run(TimerQueue.java:171)
at java.lang.Thread.run(Thread.java:724)
"SwingWorker-pool-1-thread-1" daemon prio=5 tid=0x00007fae11194800 nid=0xb92f waiting on condition [0x0000000159f82000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x000000013ac2c340> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2043)
at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1068)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:724)
"Java2D Disposer" daemon prio=5 tid=0x00007fae11183000 nid=0xac03 in Object.wait() [0x0000000157915000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000013ad250c0> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:135)
- locked <0x000000013ad250c0> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:151)
at sun.java2d.Disposer.run(Disposer.java:145)
at java.lang.Thread.run(Thread.java:724)
"Java2D Queue Flusher" daemon prio=5 tid=0x00007fae11106800 nid=0x9f0b in Object.wait() [0x0000000156eb0000]
java.lang.Thread.State: TIMED_WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000013ac57f18> (a sun.java2d.opengl.OGLRenderQueue$QueueFlusher)
at sun.java2d.opengl.OGLRenderQueue$QueueFlusher.run(OGLRenderQueue.java:208)
- locked <0x000000013ac57f18> (a sun.java2d.opengl.OGLRenderQueue$QueueFlusher)
"DestroyJavaVM" prio=5 tid=0x00007fae12003000 nid=0x1007 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"AWT-EventQueue-0" prio=5 tid=0x00007fae110ec800 nid=0x9d03 waiting on condition [0x0000000156dad000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x000000013ac8b3f0> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2043)
at java.awt.EventQueue.getNextEvent(EventQueue.java:543)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:211)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:161)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:150)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:146)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:138)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:91)
"AWT-Shutdown" prio=5 tid=0x00007fae110d5800 nid=0x6e03 in Object.wait() [0x0000000154562000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000013ad26900> (a java.lang.Object)
at java.lang.Object.wait(Object.java:503)
at sun.awt.AWTAutoShutdown.run(AWTAutoShutdown.java:287)
- locked <0x000000013ad26900> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:724)
"AppKit Thread" daemon prio=5 tid=0x00007fae12120000 nid=0x2617 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Service Thread" daemon prio=5 tid=0x00007fae12081000 nid=0x5703 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread1" daemon prio=5 tid=0x00007fae12078800 nid=0x5503 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread0" daemon prio=5 tid=0x00007fae1207f000 nid=0x5303 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Signal Dispatcher" daemon prio=5 tid=0x00007fae12077800 nid=0x5103 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Finalizer" daemon prio=5 tid=0x00007fae12043000 nid=0x3e03 in Object.wait() [0x000000015283c000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000013ac08d80> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:135)
- locked <0x000000013ac08d80> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:151)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:189)
"Reference Handler" daemon prio=5 tid=0x00007fae12040800 nid=0x3c03 in Object.wait() [0x0000000152739000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000013ac08ad0> (a java.lang.ref.Reference$Lock)
at java.lang.Object.wait(Object.java:503)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:133)
- locked <0x000000013ac08ad0> (a java.lang.ref.Reference$Lock)
"VM Thread" prio=5 tid=0x00007fae12040000 nid=0x3a03 runnable
"GC task thread#0 (ParallelGC)" prio=5 tid=0x00007fae1200f800 nid=0x3603 runnable
"GC task thread#1 (ParallelGC)" prio=5 tid=0x00007fae12010000 nid=0x3803 runnable
"VM Periodic Task Thread" prio=5 tid=0x00007fae12089800 nid=0x5903 waiting on condition
JNI global references: 344
我不确定可能出了什么问题,因为我所有的 Swing 组件更新都只在 EventDispatch 线程上完成。
我正在为 Mac OS X Mountain Lion 运行 Java(TM) SE 运行时环境(内部版本 1.7.0_40-b43),这是可用的最新版本。