Esperando múltiples SwingWorkers

Por favor considere el siguiente fragmento de código:

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 { 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(); } } } 

A continuación, agregue una etiqueta al applet que muestra algunos resultados intermedios del subproceso Trabajador (utilizando métodos de publicación / proceso). Al final, la etiqueta se elimina del panel del applet. Mi pregunta es, ¿cómo podría crear varias tags, cada una con su propio hilo de trabajo, y eliminarlas cuando hayan terminado?

Gracias por adelantado.

ACTUALIZAR:

Espero que esto aclare mi pregunta. Me gustaría que las tags se eliminen de una vez, cuando todos los trabajadores hayan terminado sus tareas, no inmediatamente después de que cada trabajador haya terminado.

ACTUALIZACIÓN 2:

El siguiente código parece estar haciendo lo que necesito. Comente si lo hice de la manera correcta. Tengo la sensación de que hay algo mal. Un problema es que las tags a la derecha del botón permanecen visibles aunque se eliminen. setVisible (falso) parece resolver este problema. ¿Es esa la manera de hacerlo?

 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 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 { 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 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(); } } } } 

Tengo la intención de eliminar todas las tags juntas cuando todos los trabajadores hayan completado sus tareas.

Como se describe aquí , un CountDownLatch funciona bien en este contexto. En el ejemplo siguiente, cada trabajador invoca el latch.countDown() al finalizar, y un trabajador Supervisor bloquea en latch.await() hasta que todas las tareas se completen. Para fines de demostración, el Supervisor actualiza las tags. La remoción al por mayor, que se muestra en los comentarios, es técnicamente posible pero generalmente poco atractiva. En cambio, considere una JList o JTable .

Prueba de traba del trabajador

 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 labels = new LinkedList(); 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 { 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 { 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 values) { label.setText(values.get(values.size() - 1).toString()); } @Override protected void done() { label.setBackground(Color.green); latch.countDown(); } } } 

El código que tiene ya lo está haciendo hasta cierto punto. En realidad, debe agregar la etiqueta al panel de contenido cuando se hace clic en el botón. Algo como esto:

  JLabel label = new JLabel(); getContentPane().add(label); getContentPane().validate(); new Worker(label).execute(); 

Puede ser una buena idea poner algo de texto en la etiqueta para que realmente lo vea cuando se agrega a la pantalla.

  JLabel label = new JLabel("Hello...I am here"); 

Y finalmente, en el método doInBackground () puede agregar código para actualizar la etiqueta a medida que se ejecuta alguna tarea:

  for(int i = 0;i < 100; i++){ Thread.sleep(20); label.setText("Counting..." + i); } 

De esta manera, realmente verá la tarea ejecutándose. Si hace clic en el botón varias veces, verá varias tags y desaparecerán una vez que se haya completado la tarea.