Почему мой графический интерфейс по-прежнему зависает даже после использования SwingUtilities.invokeLater? - PullRequest
5 голосов
/ 19 ноября 2010

У меня есть это ActionListener, которое вызывается в EDT.Моя функция plot () вычислительно тяжелая, она может легко занять пять секунд.Это заставило графический интерфейс зависать, как и ожидалось.Я добавил код SwingUtilities.invokeLater, и он все еще зависает.Разве графический интерфейс не должен реагировать сейчас, когда я создаю отдельный поток для моих вычислений тяги?

final ActionListener applyListener = new ActionListener() 
        {
            @CommitingFunction
            public void actionPerformed(ActionEvent arg0) 
            {
                /*Don't do plotting in the EDT :)*/
                SwingUtilities.invokeLater(new Runnable() 
                {
                    public void run() 
                    {
                        plot();
                    }
                });
            }
        };

Ответы [ 5 ]

15 голосов
/ 19 ноября 2010

Совсем нет. InvokeLater не создает новую тему. invokeLater существует для явного указания Swing «использовать поток диспетчеризации событий для этого, но не сейчас». invoke и invokeLater существуют, чтобы позволить вам выполнять операции, которые безопасны только для потока диспетчеризации событий, из других потоков - не делая их в этих потоках, но приказывая EDT сделать их.

Ваш ActionListener будет работать очень быстро, выбрасывая Runnable в очередь отправки событий Swing. Затем, когда он зайдет так далеко, потребуется пять секунд, чтобы запустить сюжет ().

Единственный обходной путь - рефакторинг участка (). Используйте SwingWorker (или аналогичную многопоточную стратегию, но SwingWorker, вероятно, лучше для этого), чтобы фактически переместить логику plot () в другой поток. Этот поток не может безопасно рисовать что-либо, потому что он не является потоком диспетчеризации событий Swing, поэтому все его операции рисования необходимо выполнять с помощью invokeLater (). Из соображений эффективности вы должны попытаться выполнить все операции рисования за один раз в одной invokeLater (), используя результаты, сохраненные в ваших расчетах.

3 голосов
/ 19 ноября 2010

Вы делаете противоположное тому, что вы думаете. Вместо того, чтобы запускать поток вычислений вне EDT, вы явно называете его в it!

SwingUtilities.invokeLater () ставит в очередь исполняемый файл для вызова позже, в EDT! Вы хотите использовать SwingWorker вместо.

1 голос
/ 02 августа 2012

Вот то, что я сделал для приложения моей компании, это какой-то псевдокод по юридическим причинам, но суть в том, что если экран не отвечает, он перезагружает графический интерфейс.Всякий раз, когда вы используете SwingUtilities для запуска EDT, в том же блоке инициализации создайте два потока-наблюдателя.Один поток просто выполнит действие над потоком EDT, используя утилиты Swing.Другой поток будет отслеживать первый поток, чтобы увидеть, чувствует ли первый поток отзывчивый.Первый поток подтвердит отзывчивость, только если сможет выполнить очень простую команду.

установите для isEDTCheck значение true при работе в обычном режиме, false в режиме отладки (в противном случае вы будете постоянно перезагружаться.

    if (isEDTCheck) {
        new Thread("EDTHeartbeat") {
            @Override
            public void run() {
                Runnable thisThingYouDo = new Runnable() {
                    public void run() {
                        int x = 0;
                    }
                };
                while (true) {
                    // first thread says we are waiting, aka bad state
                    edtwait=true;
                    try {
                        javax.swing.SwingUtilities.invokeAndWait(thisThingYouDo);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    } catch (InvocationTargetException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    // first thread says we are not waiting, good state
                    edtwait=false;
                    try {
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
        }.start();

        new Thread("EDTValidator") {
            @Override
            public void run() {
                while (true) {
                    // is first thread in bad state?
                    if (edtwait) {
                        try {
                            Thread.sleep(3000);
                            // after 3 seconds are we still in bad state?  if so, get rid of initial frame, pop up a dialog box in AWT that does no commands
                            if (edtwait) {
                                mainFrame.setVisible(false);
                                new Dialog();  
                            } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
        }.start();
    }


  public class Dialog extends Frame {
    private static final int WIDTH = 400;
    private static final int HEIGHT = 300;
    Frame f = null;
    public Dialog() {
        f = this;
        hasSomethingBeenEntered=false;
        this.setTitle("APP PROBLEM DETECTED");
        this.setSize(WIDTH, HEIGHT);
        this.setLocation((int)Toolkit.getDefaultToolkit().getScreenSize().getWidth() - myapp.width, 0);
        Panel p1 = new Panel() {
            @Override
            public void paint(final Graphics g) {
                int left = Dialog.WIDTH/2 - 45; // don't use WIDTH shadowed by Panel class
                int top = Dialog.HEIGHT/2 - 20; // same as above
                g.drawString("APP HAS DETECTED A PROBLEM", left, top);
            }
        };
        this.add("Center", p1);

        this.setAlwaysOnTop(true);
                 TextArea tb = new TextArea("APP HAS DETECTED A MAJOR PROBLEM\nIT WILL NOW RESTART IN 5 SECONDS");
        this.add(tb);
        this.setVisible(true);
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        restartApp();

    }

    private void restartApp() {
            Runtime.getRuntime().exec("cmd /c start cmd.exe /K \"cd C:\\Progra~1\\Common~1 && C:\\Progra~1\\Common~1\\MyAppDir\\myjavaapp.jar\"");
            System.exit(0);
       }
1 голос
/ 19 ноября 2010

Вы не показываете, что находится внутри функции plot (), но вы должны , а не поместить туда какую-либо картину.Вычислите все, что вы хотите в новой теме, и сделайте рисование в EDT.Для этого лучше использовать SwingWorker

1 голос
/ 19 ноября 2010

invokeLater добавить задачу в рабочую очередь графического интерфейса. Он будет вызван после выполнения всех других задач, однако он все еще использует поток графического интерфейса.

Я предлагаю вам взглянуть на использование ExecutorService.

Как предполагает @Adam, любой фактический рисунок, который он делает, должен быть выполнен через invokeLater.

...