2012-07-06 10 views
10

Bitte das folgende Codefragment berücksichtigen:Warten auf mehr 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(); 
     } 
    } 
} 

Hier ist ein Label des Applet hinzuzufügen, dass einige Zwischenergebnisse des Worker-Thread zeigt (mit Publish/Prozessmethoden). Am Ende wird das Label aus dem Applet-Bereich entfernt. Meine Frage ist, wie kann ich mehrere Etiketten erstellen, jeder mit seinem eigenen Worker-Thread, und sie entfernen, wenn sie alle fertig sind?

Vielen Dank im Voraus.

UPDATE:

Ich hoffe, das meine Frage zu klären. Ich möchte, dass die Etiketten auf einmal entfernt werden, wenn alle Arbeiter ihre Arbeit beendet haben, nicht sofort nachdem jeder Arbeiter fertig ist.

UPDATE 2:

Das scheint folgender Code zu tun, was ich brauche. Bitte kommentieren Sie, ob ich es richtig gemacht habe. Ich habe das Gefühl, dass etwas nicht stimmt. Ein Problem besteht darin, dass die Beschriftungen rechts neben der Schaltfläche sichtbar bleiben, obwohl sie entfernt werden. setVisible (false) scheint dieses Problem zu lösen. Ist das der Weg, es zu tun?

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

Sie könnten auch erwägen, sie in einer Liste ('JList') setzen. –

+0

@AndrewThompson, Yep, aber wo sollte ich den Code platzieren, der sie warten wird? Sollte es ein anderer SwingWorker sein, der mehrere andere hervorbringt? – Vlad

+0

Bitte nicht Ausnahmen verschlucken. – trashgod

Antwort

14

Ich beabsichtige, alle Etiketten zusammen zu entfernen, wenn alle Arbeiter ihre Aufgaben abgeschlossen haben.

Wie beschrieben here funktioniert ein CountDownLatch in diesem Zusammenhang gut. Im folgenden Beispiel ruft jeder Worker nach Abschluss latch.countDown() auf und ein Supervisor Worker blockiert latch.await(), bis alle Aufgaben abgeschlossen sind. Zu Demonstrationszwecken aktualisiert die Supervisor die Etiketten. Der in den Kommentaren gezeigte Entnahmebegriff ist technisch möglich, aber in der Regel unattraktiv. Betrachten Sie stattdessen eine JList oder 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

gutes Beispiel, gute Eins – mKorbel

+0

Vielen Dank! – Vlad

+1

Gern geschehen; Ein hybrides Applet/eine solche Anwendung bietet eine Vielzahl von Bereitstellungsoptionen mit [tag: java-web-start], wie hier [hier] (https://sites.google.com/site/drjohnbmatthews/subway) gezeigt. – trashgod

1

Der Code, den Sie haben, macht das bereits zu einem gewissen Grad. Sie müssen das Label tatsächlich dem Inhaltspaneel hinzufügen, wenn auf die Schaltfläche geklickt wird. Etwas wie folgt aus:

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

Es kann eine gute Idee sein, einen Text in das Etikett zu setzen, so dass Sie es tatsächlich sehen, wenn es um den Bildschirm hinzugefügt wird.

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

Und schließlich in der doInBackground() -Methode Sie einen Code hinzufügen kann das Etikett als eine Aufgabe zu aktualisieren ausgeführt wird:

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

So kann man eigentlich die Aufgabe Laufen sehen. Wenn Sie mehrmals auf die Schaltfläche klicken, werden mehrere Labels angezeigt, die nach Abschluss der Aufgabe verschwinden.

+0

Danke für die schnelle Antwort. Ich habe den Code vor dem Einfügen vereinfacht. Die Beschriftung enthält tatsächlich einen Text und wird dem Inhaltsbereich hinzugefügt. :) Danke für Anregungen. Ich glaube, ich habe meine Frage nicht klar formuliert. Was ich beabsichtige ist, alle Etiketten zusammen zu entfernen, wenn alle ihre Arbeiter die Aufgaben erledigt haben – Vlad