SwingWorker没有响应
我想做什么?
单击Start JButton
, SwingWorker
将执行。 在doInBackground()
方法中,我将arrNames
每个索引传递给publish()
方法,以便它可以显示在JTextArea
。
发生了什么 ?
如果我不保持行System.out.format("Counter : %d%n", counter);
作为注释 ,在我的SwingWorker
doInBackground()
方法中,然后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 { 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 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()
,引发了上述exception:
@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; }
如果我添加Thread.sleep(…),它确实有效,但它会抛出InterruptedException
显然产生exception的代码(从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++; }
但是,原因是取消worker的代码(在actionListener中):
backgroundTask.cancel(true);
它明确告诉工人取消..中断线程。 从它的api doc:
mayInterruptIfRunning – 如果执行此任务的线程应该被中断,则为true; 否则,允许完成正在进行的任务
旁白:捕获exception并且什么都不做(从而有效地忽略中断)并不是最好的想法。 在这种情况下可能不会造成太大损害 – 由于检查取消状态。 典型的工作者实现在捕获和返回之后,如果需要进行一些内部清理,或根本不处理它。
放大其他答案,不要从后台线程更新GUI,这会阻止EDT,并且不要试图避免invokeLater()
的问题。 相反, publish()
所需的结果并在process()
更新statusLabel
和tArea
,如下所示。 对于测试, Thread.sleep(100)
模拟一个小延迟,但您可以使用Thread.yield()
,如下所示。 您还可以在PropertyChangeListener
更新GUI,如下所示。
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 { 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 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是Future,非常糟糕的实现,意味着没有通知器,你把东西放到管子里等待另一边,
-
看起来好像在这个管的第一面和第二面之间没什么,这就是为什么我试图从doInBackground调用Thread,Runnable,Executor(Runnable),并忽略发布,进程,setProcess
-
另一个有趣的问题是get()和exception(s)都是exception,而不仅仅是1st。 从这个管的一侧和第二侧
-
有两种方法可以使用SwingWokrer
-
尽量避免使用SwingWorker
-
使用doInBackground作为工作线程的桥梁,输出使用publish,process,setProcess,等待done(),并使用done()作为通知器获取exception,通知SwingWorker结束
-