Waiting for multiple SwingWorkers
I intend to remove all of the labels together when all of the workers have completed their tasks.
As described here, a CountDownLatch
works well in this context. In the example below, each worker invokes latch.countDown()
on completion, and a Supervisor
worker blocks on latch.await()
until all tasks complete. For demonstration purposes, the Supervisor
updates the labels. Wholesale removal, shown in comments, is technically possible but generally unappealing. Instead, consider a JList
or 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://stackoverflow.com/a/11372932/230513* @see https://stackoverflow.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(); } }}
The code that you have is already doing that to a certain extent. You need to actually add the label to the contentpane when the button is clicked. Something like this:
JLabel label = new JLabel(); getContentPane().add(label); getContentPane().validate(); new Worker(label).execute();
It may be a good idea to put some text in the label so you actually see it when it is added to the screen.
JLabel label = new JLabel("Hello...I am here");
And finally in the doInBackground() method you can add some code to update the label as some task is running:
for(int i = 0;i < 100; i++){ Thread.sleep(20); label.setText("Counting..." + i); }
This way you actually see the task running. If you click the button multiple times you see multiple labels and they each disappear after the task is completed.