2012-07-06 16 views
10

perche il seguente frammento di codice:attesa per più 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(); 
     } 
    } 
} 

Ecco aggiungere un'etichetta per l'applet che mostra alcuni risultati intermedi del filo lavoratore (usando pubblica/metodi di lavorazione). Alla fine, l'etichetta viene rimossa dal riquadro dell'applet. La mia domanda è: come posso creare diverse etichette, ognuna con il proprio thread di lavoro, e rimuoverle quando sono tutte fatte?

Grazie in anticipo.

UPDATE:

Spero che questo permetterà di chiarire la mia domanda. Mi piacerebbe che le etichette venissero rimosse tutte in una volta, quando tutti i lavoratori hanno finito i loro compiti, non immediatamente dopo che ciascun lavoratore ha finito.

UPDATE 2:

Il seguente codice sembra stia facendo quello che mi serve. Si prega di commentare se l'ho fatto nel modo giusto. Ho la sensazione che ci sia qualcosa di sbagliato. Un problema è che le etichette a destra del pulsante rimangono visibili anche se vengono rimosse. setVisible (false) sembra risolvere questo problema. È questo il modo di farlo?

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

si potrebbe anche prendere in considerazione la loro messa in un elenco ('JList'). –

+0

@AndrewThompson, Sì, ma dove dovrei posizionare il codice che li aspetterà? Dovrebbe essere un altro SwingWorker che genera altri? – Vlad

+0

Si prega di non ingoiare le eccezioni. – trashgod

risposta

14

Ho intenzione di rimuovere tutte le etichette insieme quando tutti i lavoratori hanno completato i loro compiti.

Come descritto here, un CountDownLatch funziona correttamente in questo contesto. Nell'esempio riportato di seguito, ogni operatore richiama latch.countDown() al completamento e un blocco di lavoro Supervisor su latch.await() fino al completamento di tutte le attività. A scopo dimostrativo, lo Supervisor aggiorna le etichette. La rimozione all'ingrosso, mostrata nei commenti, è tecnicamente possibile ma generalmente poco attraente. Invece, si consideri 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

ottimo esempio, buono – mKorbel

+0

Grazie mille! – Vlad

+1

Prego; un'applet/applicazione ibrida come questa offre una varietà di opzioni di implementazione utilizzando [tag: java-web-start], come mostrato [qui] (https://sites.google.com/site/drjohnbmatthews/subway). – trashgod

1

Il codice che hai già lo sta facendo in una certa misura. È necessario aggiungere effettivamente l'etichetta al contenuto quando si fa clic sul pulsante. Qualcosa di simile a questo:

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

Può essere una buona idea di mettere un po 'di testo nel etichetta in modo che effettivamente vede quando viene aggiunto alla schermata.

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

E infine nel metodo doInBackground() è possibile aggiungere un po 'di codice per aggiornare l'etichetta come un compito è in esecuzione:

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

In questo modo si vede in realtà il compito in esecuzione. Se si fa clic sul pulsante più volte, vengono visualizzate più etichette e ciascuna scompare dopo il completamento dell'attività.

+0

Grazie per la risposta veloce. Ho semplificato il codice prima di incollarlo qui. L'etichetta ha effettivamente del testo e viene aggiunta al riquadro del contenuto. :) Grazie per i suggerimenti però. Penso di non aver espresso chiaramente la mia domanda. Quello che intendo è rimuovere tutte le etichette quando tutti i loro lavoratori hanno completato le attività – Vlad

Problemi correlati