SwingWorker не отвечает

Что я пытаюсь сделать?

По щелчку Start JButton, SwingWorker выполнит. Внутри doInBackground() метод, я передаю каждый индекс arrNamesк publish() метод, так что он может быть отображен внутри JTextArea,

Что случилось?

Если я не буду держать линию System.out.format("Counter : %d%n", counter); как комментарий, по моему doInBackground() метод SwingWorkerтогда SwingWorker работает как положено. Хотя, если я это закомментирую, то SwingWorker перестает отвечать.

Я делаю что-то неправильно?


Версия Java:

java version "1.7.0_25"
Java(TM) SE Runtime Environment (build 1.7.0_25-b16)
Java HotSpot(TM) Client VM (build 23.25-b01, mixed mode, sharing)

Вот код, который я использую:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class SwingWorkerExample1
{
    private JLabel statusLabel;
    private JTextArea tArea;
    private JButton startButton;
    private JButton stopButton;

    private BackgroundTask backgroundTask;

    private ActionListener buttonActions =
                            new ActionListener()
    {
        @Override
        public void actionPerformed(ActionEvent ae)
        {
            JButton source = (JButton) ae.getSource();
            if (source == startButton)
            {
                startButton.setEnabled(false);
                stopButton.setEnabled(true);
                backgroundTask = new BackgroundTask();
                backgroundTask.execute();
            }
            else if (source == stopButton)
            {
                backgroundTask.cancel(true);
                stopButton.setEnabled(false);
                startButton.setEnabled(true);
            }
        }
    };

    private void displayGUI()
    {
        JFrame frame = new JFrame("Swing Worker Example");
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

        JPanel contentPane = new JPanel();
        contentPane.setBorder(
            BorderFactory.createEmptyBorder(5, 5, 5, 5));
        contentPane.setLayout(new BorderLayout(5, 5));

        statusLabel = new JLabel("Status Bar", JLabel.CENTER);

        tArea = new JTextArea(20, 20);
        tArea.setWrapStyleWord(true);
        tArea.setLineWrap(true);        
        JScrollPane textScroller = new JScrollPane();
        textScroller.setBorder(
            BorderFactory.createTitledBorder("Textual OUTPUT : "));
        textScroller.setViewportView(tArea);

        startButton = new JButton("Start");
        startButton.addActionListener(buttonActions);
        stopButton = new JButton("Stop");
        stopButton.setEnabled(false);
        stopButton.addActionListener(buttonActions);
        JPanel buttonPanel = new JPanel();
        buttonPanel.add(startButton);
        buttonPanel.add(stopButton);

        contentPane.add(statusLabel, BorderLayout.PAGE_START);
        contentPane.add(textScroller, BorderLayout.CENTER);
        contentPane.add(buttonPanel, BorderLayout.PAGE_END);

        frame.setContentPane(contentPane);
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }

    private class BackgroundTask extends SwingWorker<Void, String>
    {
        private int counter = 0;

        private String[] arrNames = { "US Rates Strategy Cash",
            "Pavan Wadhwa(1-212) 844-4597", "Srini Ramaswamy(1-212) 844-4983",
            "Meera Chandan(1-212) 855-4555", "Kimberly Harano(1-212) 823-4996",
            "Feng Deng(1-212) 855-2555", "US Rates Strategy Derivatives",
            "Srini Ramaswamy(1-212) 811-4999",
            "Alberto Iglesias(1-212) 898-5442",
            "Praveen Korapaty(1-212) 812-3444", "Feng Deng(1-212) 812-2456",
            "US Rates Strategy Derivatives", "Srini Ramaswamy(1-212) 822-4999",
            "Alberto Iglesias(1-212) 822-5098",
            "Praveen Korapaty(1-212) 812-3655", "Feng Deng(1-212) 899-2222" };

        public BackgroundTask()
        {
            statusLabel.setText((this.getState()).toString());
            System.out.println(this.getState());
        }

        @Override
        protected Void doInBackground()
        {
            statusLabel.setText((this.getState()).toString());
            System.out.println(this.getState());
            while (!isCancelled())
            {
                counter %= arrNames.length;
                //System.out.format("Counter : %d%n", counter);
                publish(arrNames[counter]);
                counter++;
            }
            statusLabel.setText((this.getState()).toString());
            System.out.println(this.getState());
            return null;
        }

        @Override
        protected void process(java.util.List<String> messages)
        {
            for (String message : messages)
                tArea.append(String.format(message + "%n"));
        }
    }

    public static void main(String[] args)
    {
        Runnable runnable = new Runnable()
        {
            @Override
            public void run()
            {
                new SwingWorkerExample1().displayGUI();
            }
        };
        EventQueue.invokeLater(runnable);
    }
}

РЕДАКТИРОВАТЬ 1:

Как и предполагалось, если я добавлю Thread.sleep(...), это работает, хотя, он бросает InterruptedException как показано ниже. Так что трюк работает. Но это законный способ выполнить это?

C:\Mine\JAVA\J2SE\classes>java SwingWorkerExample1
PENDING
STARTED
java.lang.InterruptedException: sleep interrupted
        at java.lang.Thread.sleep(Native Method)
        at SwingWorkerExample1$BackgroundTask.doInBackground(SwingWorkerExample1.java:108)
        at SwingWorkerExample1$BackgroundTask.doInBackground(SwingWorkerExample1.java:76)
        at javax.swing.SwingWorker$1.call(SwingWorker.java:296)
        at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
        at java.util.concurrent.FutureTask.run(FutureTask.java:166)
        at javax.swing.SwingWorker.run(SwingWorker.java:335)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
        at java.lang.Thread.run(Thread.java:724)
DONE

РЕДАКТИРОВАТЬ 2:

Только doInBackground() изменено, что подняло вышеупомянутое исключение:

@Override
protected Void doInBackground()
{
    Runnable runnable = new Runnable()
    {
        @Override
        public void run()
        {
            statusLabel.setText((BackgroundTask.this.getState()).toString());
        }
    };
    EventQueue.invokeLater(runnable);

    System.out.println(this.getState());
    while (!isCancelled())
    {
        counter %= arrNames.length;             
        //System.out.format("Counter : %d%n", counter);
        publish(arrNames[counter]);
        try
        {Thread.sleep(30);}
        catch(InterruptedException ie)
        {ie.printStackTrace();}
        counter++;
    }
    runnable = new Runnable()
    {
        @Override
        public void run()
        {
            statusLabel.setText((BackgroundTask.this.getState()).toString());
        }
    };
    EventQueue.invokeLater(runnable);
    System.out.println(this.getState());
    return null;
}

3 ответа

Решение

если я добавлю Thread.sleep(...), он будет работать, но он выдаст исключение InterruptedException

Код, который, по-видимому, создает исключение (скопировано из редактирования OP):

while (!isCancelled()) {
    counter %= arrNames.length;
    // System.out.format("Counter : %d%n", counter);
    publish(arrNames[counter]);
    try {
        Thread.sleep(30); // throws
    } catch (InterruptedException ie) {
        ie.printStackTrace();
    }
    counter++;
}

Причина, однако, в том, что код отменяет работника (в actionListener):

backgroundTask.cancel(true);

который явно указывает работнику на отмену путем прерывания потока. Из его api doc:

mayInterruptIfRunning - true, если поток, выполняющий эту задачу, должен быть прерван; в противном случае выполняемые задачи могут быть выполнены

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

Усиливая другие ответы, не обновляйте GUI из фонового потока, который блокирует EDT, и не пытайтесь избежать проблемы с invokeLater(), Вместо, publish() желаемый результат и обновить как statusLabel а также tArea в process(), как предлагается ниже. Для тестирования, Thread.sleep(100) имитирует небольшую задержку, но вы можете использовать Thread.yield(), как показано здесь. Вы также можете обновить графический интерфейс в PropertyChangeListener, как показано здесь.

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.*;
import javax.swing.*;

public class SwingWorkerExample1 {

    private JLabel statusLabel;
    private JTextArea tArea;
    private JButton startButton;
    private JButton stopButton;
    private BackgroundTask backgroundTask;
    private ActionListener buttonActions = new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent ae) {
            JButton source = (JButton) ae.getSource();
            if (source == startButton) {
                startButton.setEnabled(false);
                stopButton.setEnabled(true);
                backgroundTask = new BackgroundTask();
                backgroundTask.execute();
            } else if (source == stopButton) {
                backgroundTask.cancel(true);
                stopButton.setEnabled(false);
                startButton.setEnabled(true);
            }
        }
    };

    private void displayGUI() {
        JFrame frame = new JFrame("Swing Worker Example");
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

        JPanel contentPane = new JPanel();
        contentPane.setBorder(
            BorderFactory.createEmptyBorder(5, 5, 5, 5));
        contentPane.setLayout(new BorderLayout(5, 5));

        statusLabel = new JLabel("Status Bar", JLabel.CENTER);

        tArea = new JTextArea(20, 20);
        tArea.setWrapStyleWord(true);
        tArea.setLineWrap(true);
        JScrollPane textScroller = new JScrollPane();
        textScroller.setBorder(
            BorderFactory.createTitledBorder("Textual OUTPUT : "));
        textScroller.setViewportView(tArea);

        startButton = new JButton("Start");
        startButton.addActionListener(buttonActions);
        stopButton = new JButton("Stop");
        stopButton.setEnabled(false);
        stopButton.addActionListener(buttonActions);
        JPanel buttonPanel = new JPanel();
        buttonPanel.add(startButton);
        buttonPanel.add(stopButton);

        contentPane.add(statusLabel, BorderLayout.PAGE_START);
        contentPane.add(textScroller, BorderLayout.CENTER);
        contentPane.add(buttonPanel, BorderLayout.PAGE_END);

        frame.setContentPane(contentPane);
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }

    private class BackgroundTask extends SwingWorker<Void, String> {

        private int counter = 0;
        private String[] arrNames = {"US Rates Strategy Cash",
            "Pavan Wadhwa(1-212) 844-4597", "Srini Ramaswamy(1-212) 844-4983",
            "Meera Chandan(1-212) 855-4555", "Kimberly Harano(1-212) 823-4996",
            "Feng Deng(1-212) 855-2555", "US Rates Strategy Derivatives",
            "Srini Ramaswamy(1-212) 811-4999",
            "Alberto Iglesias(1-212) 898-5442",
            "Praveen Korapaty(1-212) 812-3444", "Feng Deng(1-212) 812-2456",
            "US Rates Strategy Derivatives", "Srini Ramaswamy(1-212) 822-4999",
            "Alberto Iglesias(1-212) 822-5098",
            "Praveen Korapaty(1-212) 812-3655", "Feng Deng(1-212) 899-2222"};

        public BackgroundTask() {
            statusLabel.setText((this.getState()).toString());
        }

        @Override
        protected Void doInBackground() {
            while (!isCancelled()) {
                counter %= arrNames.length;
                publish(arrNames[counter]);
                counter++;
                try {
                    Thread.sleep(100); // simulate latency
                } catch (InterruptedException ex) {
                    publish("Cancelled: " + isCancelled());
                }
            }
            return null;
        }

        @Override
        protected void process(java.util.List<String> messages) {
            statusLabel.setText((this.getState()).toString());
            for (String message : messages) {
                tArea.append(String.format(message + "%n"));
            }
        }
    }

    public static void main(String[] args) {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                new SwingWorkerExample1().displayGUI();
            }
        };
        EventQueue.invokeLater(runnable);
    }
}

Комментарии

  • @kleopatra - это ошибка документации, исправленная, по крайней мере, в jdk7:-), пожалуйста, какой из JDK эти ошибки показаны как из карусели....,

  • ожидание / уведомление для потока, SwingWorker - это будущее, очень плохо реализовано, означает, что без уведомлений вы что-то кладете в трубу и ждете с другой стороны,

  • кажется, что ничто не находится между одной и второй стороной этой трубки, по этой причине я попытался вызвать Thread, Runnable, Executor(Runnable) из doInBackground и игнорировать publish, process, setProcess

  • другая забавная проблема - получить () и исключение (я) все исключения, а не только 1-й. с одной и второй стороны этой трубки

  • Есть два способа использования SwingWokrer

    1. старайтесь избегать использовать SwingWorker

    2. использовать doInBackground в качестве моста для рабочего потока, для вывода использовать publish, process, setProcess, wait for done() и done () в качестве уведомителя для получения исключения, уведомителя о завершении SwingWorker

Другие вопросы по тегам