如何使用备用事件队列显示Java对话框?

我有一些代码可以检测
Java AWT事件队列是否被冻结(忙于处理某个事件或等待锁定)一段时间过长,因为有些错误的外国代码无法使用SwingWorker或类似的东西,我想提供给恢复.只是使用Thread.stop杀死事件派发线程,但它可能是危险的(并且EQ可能因为计算机暂时过载而被阻止),所以我宁愿提示用户进行确认.但是,显示一个对话框需要等待EQ被解除阻塞,这正是不可能的.

从一个合理的可移植Java程序,有没有办法显示一个对话框(或任何一种可以响应输入事件的UI元素),而不涉及正常的事件队列?

我已经尝试过运行SystemTray.add(TrayIcon),它确实设法在从另一个线程调用时显示一个托盘项…但是图标没有绘制,并且ActionEvents没有交付,所以这没有帮助.

它似乎也可以显示一个新的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() {}
}

最佳答案 AWT API的编写就好像只有事件队列一样.因此,我们正在谈论可变的静态,因此糟糕的设计和邪恶的黑客.

Sun PlugIn和WebStart使用未记录的API来破解上下文.查看AppContext类.首先由ThreadGroup查找上下文,如果这是不确定的,那么通过检查堆栈上的ClassLoaders.

明显的评论:你当然可以运行一个单独的过程.大多数应用程序只是确保它们不会阻止包括EDT在内的关键资源.

点赞