等待多个 SwingWorker
Posted
技术标签:
【中文标题】等待多个 SwingWorker【英文标题】:Waiting for multiple SwingWorkers 【发布时间】:2012-07-07 03:44:51 【问题描述】:请考虑以下代码片段:
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.reflect.InvocationTargetException;
import javax.swing.*;
public class TestApplet extends JApplet
@Override
public void init()
try
SwingUtilities.invokeAndWait(new Runnable()
@Override
public void run()
createGUI();
);
catch(InterruptedException | InvocationTargetException ex)
private void createGUI()
getContentPane().setLayout(new FlowLayout());
JButton startButton = new JButton("Do work");
startButton.addActionListener(new ActionListener()
@Override
public void actionPerformed(ActionEvent ae)
JLabel label = new JLabel();
new Worker(label).execute();
);
getContentPane().add(startButton);
private class Worker extends SwingWorker<Void, Void>
JLabel label;
public Worker(JLabel label)
this.label = label;
@Override
protected Void doInBackground() throws Exception
// do work
return null;
@Override
protected void done()
getContentPane().remove(label);
getContentPane().revalidate();
这里是为小程序添加一个标签,显示工作线程的一些中间结果(使用发布/处理方法)。最后,标签从小程序的窗格中删除。我的问题是,如何创建多个标签,每个标签都有自己的 Worker 线程,并在它们全部完成后将它们删除?
提前致谢。
更新:
我希望这能澄清我的问题。我希望在所有工人完成任务后立即移除标签,而不是在每个工人完成后立即移除。
更新 2:
以下代码似乎正在做我需要的事情。请评论我是否以正确的方式做到了。我有一种感觉有什么不对劲。一个问题是按钮右侧的标签虽然被移除,但仍然可见。 setVisible(false) 似乎解决了这个问题。是这样的吗?
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.reflect.InvocationTargetException;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.swing.*;
public class TestApplet extends JApplet
private Queue<JLabel> labels = new LinkedList<>();
private static final Random rand = new Random();
@Override
public void init()
try
SwingUtilities.invokeAndWait(new Runnable()
@Override
public void run()
createGUI();
);
catch(InterruptedException | InvocationTargetException ex)
private void createGUI()
getContentPane().setLayout(new FlowLayout());
JButton startButton = new JButton("Do work");
startButton.addActionListener(new ActionListener()
@Override
public void actionPerformed(ActionEvent ae)
ExecutorService executor = Executors.newFixedThreadPool(10);
for(int i = 0; i < 10; i++)
JLabel label = new JLabel();
getContentPane().add(label);
executor.execute(new Counter(label));
);
getContentPane().add(startButton);
private class Counter extends SwingWorker<Void, Integer>
private JLabel label;
public Counter(JLabel label)
this.label = label;
@Override
protected Void doInBackground() throws Exception
for(int i = 1; i <= 100; i++)
publish(i);
Thread.sleep(rand.nextInt(80));
return null;
@Override
protected void process(List<Integer> values)
label.setText(values.get(values.size() - 1).toString());
@Override
protected void done()
labels.add(label);
if(labels.size() == 10)
while(!labels.isEmpty())
getContentPane().remove(labels.poll());
getContentPane().revalidate();
【问题讨论】:
您也可以考虑将它们放在一个列表中 (JList
)。
@AndrewThompson,是的,但是我应该把等待他们的代码放在哪里?应该是另一个 SwingWorker 产生几个其他的吗?
请不要吞下异常。
@trashgod 谢谢。确实。这只是一个例子。
【参考方案1】:
我打算在所有工人完成任务后一起移除所有标签。
如here 所述,CountDownLatch
在这种情况下效果很好。在下面的示例中,每个工作人员在完成时调用latch.countDown()
,并且Supervisor
工作人员在latch.await()
上阻塞,直到所有任务完成。出于演示目的,Supervisor
会更新标签。以 cmets 显示的批发移除在技术上是可行的,但通常没有吸引力。相反,请考虑 JList
或 JTable
。
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.swing.*;
/**
* @see https://***.com/a/11372932/230513
* @see https://***.com/a/3588523/230513
*/
public class WorkerLatchTest extends JApplet
private static final int N = 8;
private static final Random rand = new Random();
private Queue<JLabel> labels = new LinkedList<JLabel>();
private JPanel panel = new JPanel(new GridLayout(0, 1));
private JButton startButton = new JButton(new StartAction("Do work"));
public static void main(String[] args)
EventQueue.invokeLater(new Runnable()
@Override
public void run()
JFrame frame = new JFrame();
frame.setTitle("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new WorkerLatchTest().createGUI());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
);
@Override
public void init()
EventQueue.invokeLater(new Runnable()
@Override
public void run()
add(new WorkerLatchTest().createGUI());
);
private JPanel createGUI()
for (int i = 0; i < N; i++)
JLabel label = new JLabel("0", JLabel.CENTER);
label.setOpaque(true);
panel.add(label);
labels.add(label);
panel.add(startButton);
return panel;
private class StartAction extends AbstractAction
private StartAction(String name)
super(name);
@Override
public void actionPerformed(ActionEvent e)
startButton.setEnabled(false);
CountDownLatch latch = new CountDownLatch(N);
ExecutorService executor = Executors.newFixedThreadPool(N);
for (JLabel label : labels)
label.setBackground(Color.white);
executor.execute(new Counter(label, latch));
new Supervisor(latch).execute();
private class Supervisor extends SwingWorker<Void, Void>
CountDownLatch latch;
public Supervisor(CountDownLatch latch)
this.latch = latch;
@Override
protected Void doInBackground() throws Exception
latch.await();
return null;
@Override
protected void done()
for (JLabel label : labels)
label.setText("Fin!");
label.setBackground(Color.lightGray);
startButton.setEnabled(true);
//panel.removeAll(); panel.revalidate(); panel.repaint();
private static class Counter extends SwingWorker<Void, Integer>
private JLabel label;
CountDownLatch latch;
public Counter(JLabel label, CountDownLatch latch)
this.label = label;
this.latch = latch;
@Override
protected Void doInBackground() throws Exception
int latency = rand.nextInt(42) + 10;
for (int i = 1; i <= 100; i++)
publish(i);
Thread.sleep(latency);
return null;
@Override
protected void process(List<Integer> values)
label.setText(values.get(values.size() - 1).toString());
@Override
protected void done()
label.setBackground(Color.green);
latch.countDown();
【讨论】:
不客气;像这样的混合小程序/应用程序使用java-web-start 提供多种部署选项,如here 所示。 +1 出色的答案和代码示例! (挑剔与问题的核心无关:ActionListener 的使用,咳嗽 :-) @kleopatra:谢谢提醒;Action
更灵活。
能否请您详细说明在这种情况下使用Action而不是ActionListener的优势?
是的,但不如相关的tutorial。【参考方案2】:
您拥有的代码已经在一定程度上做到了这一点。单击按钮时,您需要将标签实际添加到内容窗格。像这样的:
JLabel label = new JLabel();
getContentPane().add(label);
getContentPane().validate();
new Worker(label).execute();
在标签中放置一些文本可能是个好主意,以便在将其添加到屏幕时实际看到它。
JLabel label = new JLabel("Hello...I am here");
最后在 doInBackground() 方法中,您可以添加一些代码以在某些任务运行时更新标签:
for(int i = 0;i < 100; i++)
Thread.sleep(20);
label.setText("Counting..." + i);
这样您就可以真正看到任务正在运行。如果您多次单击该按钮,您会看到多个标签,并且在任务完成后它们都会消失。
【讨论】:
感谢您的快速回复。我在粘贴之前简化了代码。标签确实有一些文本并被添加到内容窗格中。 :) 感谢您的建议。我想我没有清楚地说明我的问题。我打算在所有工人完成任务后一起删除所有标签以上是关于等待多个 SwingWorker的主要内容,如果未能解决你的问题,请参考以下文章