dav*_*mac 9 java swing multithreading
我有一个Java程序,它在一个单独的(非EDT)线程上执行紧密循环.虽然我认为Swing UI仍然应该响应,但事实并非如此.下面的示例程序显示了问题:单击"试用我"按钮应该在大约一半时间后弹出一个对话框,并且应该可以通过单击其任何响应立即关闭该对话框.相反,对话框需要更长的时间才能显示,并且/或者在单击其中一个按钮后需要很长时间才能关闭.
有没有人知道为什么EDT处理被延迟,即使只有一个繁忙的线程?
(请注意,尽管Thread.sleep呼叫的各种建议是导致问题的原因,但事实并非如此.它可以被移除,问题仍然可以重现,尽管它表现得稍微不那么频繁并且通常表现出上述第二种行为 - 即非- 响应JOptionPane对话而不是延迟对话框出现.此外,没有理由睡眠调用应该屈服于另一个线程,因为有如上所述的备用处理器核心 ; EDT可以在调用后继续在另一个核心上运行sleep).
import java.awt.EventQueue;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
public class MFrame extends JFrame
{
public static void main(String[] args)
{
EventQueue.invokeLater(() -> {
new MFrame();
});
}
public MFrame()
{
JButton tryme = new JButton("Try me!");
tryme.addActionListener((e) -> {
Thread t = new Thread(() -> {
int a = 4;
for (int i = 0; i < 100000; i++) {
for (int j = 0; j < 100000; j++) {
a *= (i + j);
a += 7;
}
}
System.out.println("a = " + a);
});
t.start();
// Sleep to give the other thread a chance to get going.
// (Included because it provokes the problem more reliably,
// but not necessary; issue still occurs without sleep call).
try {
Thread.sleep(500);
}
catch (InterruptedException ie) {
ie.printStackTrace();
}
// Now display a dialog
JOptionPane.showConfirmDialog(null, "You should see this immediately");
});
getContentPane().add(tryme);
pack();
setVisible(true);
}
}
Run Code Online (Sandbox Code Playgroud)
更新:问题仅发生在服务器VM上(但请参阅进一步更新).指定客户端VM(-clientjava可执行文件的命令行参数)似乎可以抑制一台计算机上的问题(更新2),而不是另一台计算机上的问题.
更新3:单击按钮后,我看到Java进程的处理器使用率为200%,这意味着有2个处理器核心已满载.这对我来说根本没有意义.
更新4:也发生在Windows上.
更新5:使用调试器(Eclipse)证明是有问题的; 调试器似乎无法阻止线程.这是非常不寻常的,我怀疑VM中存在某种活锁或竞争条件,因此我向Oracle提交了一个错误(评论ID JI-9029194).
更新6:我在OpenJDK错误数据库中找到了我的错误报告.(我没有被告知它已被接受,我不得不搜索它).那里的讨论最有趣,并且已经阐明了可能导致这个问题的原因.
默认情况下,所有内容都从 EDT 开始(在本例中为 ),并在执行所有代码(包括 )时ActionListener锁定结束,并假设您丢失了所有事件(包括)。绘画,在这段代码的整个时间内,所有事件都在最后一次性绘制 Thread.sleepThread.sleep
此代码丢失了 autoclose JOptionPane,永远不会绘制到屏幕上(模拟如何轻松地Thread.sleep在 Swing 中终止绘制)
Swing GUI 对鼠标或按键事件不负责,不可能终止此应用程序,这仅可以从Runnable#Thread和SwingWorker开始,即指定启动新的另一个线程 ( Workers Thread),在内部的任何事情期间Runnable#Thread并且SwingWorker任务可取消(或者通过使用Runnable#Threadis可以暂停、修改...)
这与多线程无关,也不与其他核心共享资源,在 Win10 中,所有核心都向我展示,按比例共享增量
(稍微修改过的)代码的输出(基于您的 SSCCE / MCVE)
run:
test started at - 16:41:13
Thread started at - 16:41:15
to test EDT before JOptionPane - true at 16:41:16
before JOptionPane at - 16:41:16
Thread ended at - 16:41:29
a = 1838603747
isEventDispatchThread()false
after JOptionPane at - 16:41:29
Thread started at - 16:41:34
to test EDT before JOptionPane - true at 16:41:34
before JOptionPane at - 16:41:34
Thread ended at - 16:41:47
a = 1838603747
isEventDispatchThread()false
after JOptionPane at - 16:41:47
BUILD SUCCESSFUL (total time: 38 seconds)
Run Code Online (Sandbox Code Playgroud)
再次 autocloseJOptionPane永远不会被绘制到屏幕上(测试过 win10-64b、i7、Java8),可能到 Java 1.6.022 一切都会正确绘制(AFAIK 对 edt 的最后修复,从这次开始SwingWorker没有错误)
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class MFrame extends JFrame {
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
new MFrame();
});
}
public MFrame() {
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
System.out.println("test started at - " + sdf.format(getCurrDate().getTime()));
//http://stackoverflow.com/a/18107432/714968
Action showOptionPane = new AbstractAction("show me pane!") {
private static final long serialVersionUID = 1L;
@Override
public void actionPerformed(ActionEvent e) {
createCloseTimer(3).start();
System.out.println("before JOptionPane at - "
+ sdf.format(getCurrDate().getTime()));
JOptionPane.showMessageDialog((Component) e.getSource(), "nothing to do!");
}
private Timer createCloseTimer(int seconds) {
ActionListener close = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Window[] windows = Window.getWindows();
for (Window window : windows) {
if (window instanceof JDialog) {
JDialog dialog = (JDialog) window;
if (dialog.getContentPane().getComponentCount() == 1
&& dialog.getContentPane().getComponent(0) instanceof JOptionPane) {
dialog.dispose();
System.out.println("after JOptionPane at - "
+ sdf.format(getCurrDate().getTime()));
}
}
}
}
};
Timer t = new Timer(seconds * 1000, close);
t.setRepeats(false);
return t;
}
};
JButton tryme = new JButton("Try me!");
tryme.addActionListener((e) -> {
System.out.println("Thread started at - "
+ sdf.format(getCurrDate().getTime()));
Thread t = new Thread(() -> {
int a = 4;
for (int i = 0; i < 100000; i++) {
for (int j = 0; j < 100000; j++) {
a *= (i + j);
a += 7;
}
}
System.out.println("Thread ended at - "
+ sdf.format(getCurrDate().getTime()));
System.out.println("a = " + a);
System.out.println("isEventDispatchThread()" +
SwingUtilities.isEventDispatchThread());
});
t.start();
// Sleep to give the other thread a chance to get going:
try {
Thread.sleep(500);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
// Now display a dialog
System.out.println("to test EDT before JOptionPane - "
+ SwingUtilities.isEventDispatchThread()
+ " at " + sdf.format(getCurrDate().getTime()));
showOptionPane.actionPerformed(e);
});
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
add(tryme);
pack();
setLocation(150, 150);
setVisible(true);
}
private Date getCurrDate() {
java.util.Date date = new java.util.Date();
return date;
}
}
Run Code Online (Sandbox Code Playgroud)
注意也必须用Runnable#Threadand进行测试SwingWorker
| 归档时间: |
|
| 查看次数: |
947 次 |
| 最近记录: |