我有一些代码可以检测Java AWT事件队列是否被冻结(忙于处理某个事件或等待锁定)过长的时间,因为有错误的外部代码无法使用SwingWorker
或类似的,我想提供恢复。只是使用Thread.stop
杀死事件调度线程,但它可能是危险的(并且EQ可能仅仅因为计算机暂时过载而被阻止),所以我宁愿提示用户进行确认。但是,显示一个对话框需要等待EQ被解除阻塞,这正是不可能的。
从合理的可移植Java程序中,有没有办法在不涉及正常事件队列的情况下显示对话框(或任何可以响应输入事件的UI元素)?
我已经尝试过运行SystemTray.add(TrayIcon)
,它确实设法在从另一个线程调用时显示一个托盘项...但是图标没有绘制,并且ActionEvent
没有被传递,所以这是没用。
似乎也可以显示新的JFrame
,甚至可以使用paintImmediately
在其上绘制标签,但仍然没有明显的方法来接收鼠标事件。
根据Tom Hawtin的提示,我尝试创建一个新的AppContext
。在JDK 6u18上,新上下文中显示的对话框似乎正确绘制,但在主事件队列被解除阻塞之前不会接收鼠标事件,从而无法实现目的:
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.WindowConstants;
import sun.awt.AppContext;
import sun.awt.SunToolkit;
public class Main {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public @Override void run() {
new MainWindow().setVisible(true);
System.err.println("main context: " + AppContext.getAppContext());
}
});
new TrackEQ(1000*3);
}
private static class MainWindow extends JFrame {
MainWindow() {
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
JButton pause = new JButton("Pause");
pause.addActionListener(new ActionListener() {
public @Override void actionPerformed(ActionEvent e) {
try {
Thread.sleep(15000);
} catch (InterruptedException x) {
x.printStackTrace();
}
}
});
getContentPane().add(pause);
pack();
setLocation(100, 100);
}
}
private static class TrackEQ implements Runnable {
private final ScheduledExecutorService svc;
private final int timeout;
private boolean stuck = false;
private boolean wait = false;
private Thread eq;
TrackEQ(int timeout) {
this.timeout = timeout;
svc = Executors.newSingleThreadScheduledExecutor();
svc.schedule(this, 0, TimeUnit.MILLISECONDS);
}
public @Override synchronized void run() {
if (EventQueue.isDispatchThread()) {
stuck = false;
eq = Thread.currentThread();
} else {
if (stuck && !wait) {
System.err.println("UI is stuck!");
wait = true;
Map<Thread,StackTraceElement[]> stackTraces = Thread.getAllStackTraces();
StackTraceElement[] stack = stackTraces.get(eq);
if (stack != null) {
for (StackTraceElement el : stack) {
System.err.println("stuck at " + el);
}
ThreadGroup grp = new ThreadGroup("showing dialog");
grp.setDaemon(true);
new Thread(grp, new Runnable() {
public @Override void run() {
System.err.println("created new app context in " + Thread.currentThread().getThreadGroup());
SunToolkit.createNewAppContext();
EventQueue.invokeLater(new Runnable() {
public @Override void run() {
System.err.println("main EQ=" + eq + " whereas my EQ=" + Thread.currentThread());
System.err.println("will show dialog in " + AppContext.getAppContext());
final JDialog dlg = new JDialog();
dlg.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
JButton fix = new JButton("Fix!");
fix.addActionListener(new ActionListener() {
@SuppressWarnings("deprecation")
public @Override void actionPerformed(ActionEvent e) {
System.err.println("agreed to fix");
eq.stop();
wait = false;
dlg.setVisible(false);
}
});
dlg.getContentPane().add(fix);
dlg.pack();
dlg.setLocation(200, 100);
dlg.setVisible(true);
System.err.println("showed dialog");
}
});
}
}, "showing dialog").start();
} else {
System.err.println("no stack trace for " + eq + "; listed threads: " + stackTraces.keySet());
}
} else {
stuck = true;
}
EventQueue.invokeLater(this);
svc.schedule(this, timeout, TimeUnit.MILLISECONDS);
}
}
}
private Main() {}
}
答案 0 :(得分:2)
AWT API的编写就好像只有事件队列一样。因此,我们正在谈论可变的静态,因此糟糕的设计和邪恶的黑客。
Sun PlugIn和WebStart使用未记录的API来破解上下文。查看AppContext
课程。首先由ThreadGroup
查找上下文,如果这是不确定的,则通过检查堆栈上的ClassLoader
。
明显的评论:你当然可以运行一个单独的流程。大多数应用程序只是确保它们不会阻止包括EDT在内的关键资源。
答案 1 :(得分:0)
SwingWorker类在某些Java版本中可用。
它允许您在单独的线程上运行消耗大量任务。
根据您使用的JRE版本,您可以将类插入以在不同的线程上运行现有任务。
这些示例来自Wikipedia:
Pre-Java 6:
SwingWorker worker = new SwingWorker() {
public Object construct() {
... //add the code for the background thread
}
public void finished() {
... //code that you add here will run in the UI thread
}
};
或者Java 6:
SwingWorker worker = new SwingWorker<Document, Void>() {
public Document doInBackground() {
Document intDoc = loadXML();
return intDoc;
}
};