我有一个非常简单的应用程序,但有一个非常奇怪的行为。
它本质上是 SwingWorker 示例,但当我按下按钮时,GUI 的行为就像 EDT 被阻止一样。我可以同时触发两个并且它们并行运行(具有几乎相同的运行时间)但是菜单在它们运行时仍然卡住。当我使用带有可运行对象的线程时,会发生完全相同的行为。同样有趣的是,如果循环被 Thread.sleep 替换,GUI 会正常运行。
有什么想法吗?
public class DummyFrame extends JFrame {
public DummyFrame() {
JMenuBar bar = new JMenuBar();
JMenu menu = new JMenu("File");
menu.add(new JMenuItem("TEST1"));
menu.add(new JMenuItem("Test2"));
bar.add(menu);
setJMenuBar(bar);
JButton button = new JButton("FOOBAR");
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
final long start = System.currentTimeMillis();
SwingWorker<Void, Integer> testTask = new SwingWorker<Void, Integer>() {
@Override
protected Void doInBackground()
throws Exception {
int k = 0;
for (int i=0; i<200000; i++) {
for (int j=0; j<100000; j++) {
if (i==j && i%10000 == 0)
k++;
}
}
System.out.println(k+" "+(System.currentTimeMillis()-start));
return null;
}
};
testTask.execute();
}
});
getContentPane().add(button);
pack();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
DummyFrame f = new DummyFrame();
f.setVisible(true);
}
});
}
}
最佳答案
问题出在 VM 的线程实现上。规范没有具体说明这是如何完成的。 Java 线程应该映射到 native Windows 线程,然后使用 Windows 调度程序共享时间片。目前还不清楚这是不是真的发生了,所有的官方文档都只支持在 Solaris 上运行的线程信息。
我认为主要问题来自线程抢占的实现细节。这可能是由 JVM 和 native 操作系统之间的编译代码优化和抢占控制的某种组合引起的。 JVM 可以使用方法调用作为抢占线程的指向,我认为这里的部分问题是您在另一个循环之上调用一个循环。如果你用一个函数调用来分解它们,它在我的机器上的表现会好得多。我在 Windows 7 上使用 1.6.0_23 64 位服务器 VM。
SwingWorker<Void, Integer> testTask = new SwingWorker<Void, Integer>() {
private int k;
private void inc() {
this.k++;
}
private void innerLoop(int i) {
for (int j=0; j<100000; j++) {
if (i==j && i%10000 == 0)
this.inc();
}
}
@Override
protected Void doInBackground()
throws Exception {
System.out.println("Started");
for (int i=0; i<200000; i++) {
this.innerLoop(i);
}
System.out.println(k+" "+(System.currentTimeMillis()-start));
return null;
}
};
然而,在同时启动其中几个之后,即使这样也会出现问题。最好的解决方案是在每次启动内部循环时添加对 Thread.yield()
的调用。这可确保编译后的代码为调度程序提供每次迭代抢占线程的机会。
关于java - 启动 Thread 或进行计算的 SwingWorker 时的 Swing 伪 block ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4881609/