2012-07-06 14 views
10

por que no el fragmento de código siguiente:Esperándole múltiples SwingWorkers

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

Aquí es añadir una etiqueta al applet que muestra algunos resultados intermedios del subproceso de trabajo (usando publicar/métodos de proceso). Al final, la etiqueta se elimina del panel del applet. Mi pregunta es, ¿cómo podría crear varias etiquetas, cada una con su propio hilo de trabajo, y eliminarlas cuando hayan terminado?

Gracias de antemano.

ACTUALIZACIÓN:

espero que esto aclare mi pregunta. Me gustaría que las etiquetas 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 etiquetas 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<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(); 
      } 
     } 
    } 
} 
+0

También puede considerar ponerlos en una lista ('JList'). –

+0

@AndrewThompson, Sí, ¿pero dónde debería colocar el código que los esperará? ¿Debería ser otro SwingWorker generando varios otros? – Vlad

+0

No tragues las excepciones. – trashgod

Respuesta

14

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

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

Worker Latch Test

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

gran ejemplo, una buena – mKorbel

+0

muchas gracias! – Vlad

+1

De nada; un híbrido del applet/aplicación como ésta ofrece una variedad de opciones de despliegue utilizando [tag: java-web-Inicio], como se muestra [aquí] (https://sites.google.com/site/drjohnbmatthews/subway). – trashgod

1

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 algún texto en la etiqueta de lo que en realidad se ve cuando se añade a la pantalla.

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

Y, finalmente, en el método doInBackground() se puede añadir algo de código para actualizar la etiqueta como una tarea está en ejecución:

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

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

+0

Gracias por la respuesta rápida. Simplifiqué el código antes de pegarlo aquí. La etiqueta de hecho tiene algo de texto y se agrega al panel de contenido. :) Gracias por las sugerencias. Creo que no formulé mi pregunta claramente. Lo que pretendo es eliminar todas las etiquetas juntos cuando la totalidad de sus trabajadores han completado las tareas – Vlad

Cuestiones relacionadas