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
старайтесь избегать использовать SwingWorker
использовать doInBackground в качестве моста для рабочего потока, для вывода использовать publish, process, setProcess, wait for done() и done () в качестве уведомителя для получения исключения, уведомителя о завершении SwingWorker