SwingWorker没有响应

我想做什么?

单击Start JButtonSwingWorker将执行。 在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()更新statusLabeltArea ,如下所示。 对于测试, 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

    1. 尽量避免使用SwingWorker

    2. 使用doInBackground作为工作线程的桥梁,输出使用publish,process,setProcess,等待done(),并使用done()作为通知器获取exception,通知SwingWorker结束