Downloads

SwingWorker Tutorial
Monday, 10 March 2008 01:02

Jeder kennt folgendes Problem: Man arbeitet mit einer Anwendung, führt eine Aktion aus und plötzlich reagiert die GUI nicht mehr. Das Programm scheint "eingefroren" zu sein. Nach einiger Zeit reagiert das Programm wieder und die gewünschte Aktion wurde ausgeführt.

Grund hierfür kann sein, dass die Aktion im sog. Event Dispatcher Thread (EDT) ausgeführt wurde, also dem Thread der auch für die GUI zuständig ist. Ist die Aktion rechen- und zeitaufwändig so können zwischenzeitlich keine anderen Aktionen, wie das Verschieben des Programmfensters, ausgeführt werden. Das Programm "hängt".

Um das Problem zu lösen hat Java seit Version 6 den sog. SwingWorker integriert. Dadurch lassen sich rechen- und zeitaufwändige Prozesse auslagern und blockieren so nicht mehr den EDT.

Mit Hilfe eines Beispiels zeigt dieses Tutorial den Einsatz des SwingWorkers. Benötigt wird zwingend Java 6. Im folgenden Beispiel wird ein simples Programm mit graphischer Oberfläche erstellt. Aufgabe des Programms ist es, eine vorgegebene Primzahl zu berechnen. Beispielsweise ist die fünfte Primzahl 11 (2,3,5,7,11). Die Funktionsweise der Berechnung ist für das Tutorial unwichtig; Die Primzahlenberechnung wurde gewählt, da die Berechnung sehr rechen- und zeitintensiv sein kann (vor allem wenn sie so ineffizient programmiert wurde wie hier ;-)).

Zuerst das Programm ohne SwingWorker (Download: Demo1.java ):

 
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;
 
public class Demo1 extends JFrame implements ActionListener{
  JLabel jlabe_text,jlabe_result;
  JTextField jtext_input;
  JButton jbutt_calc;
  /**
   * Init the GUI elements
   */
  public void initComponents(){
    jlabe_text = new JLabel("Calculate prime:");
    jtext_input = new JTextField();
    jlabe_result = new JLabel();
    jbutt_calc = new JButton("Calc");
    jbutt_calc.addActionListener(this);
    jbutt_calc.setActionCommand("calc");
 
    getContentPane().setLayout(new GridLayout(1,4));
    getContentPane().add(jlabe_text);
    getContentPane().add(jtext_input);
    getContentPane().add(jbutt_calc);
    getContentPane().add(jlabe_result);
 
    pack();
    setSize(400,60);
    setVisible(true);
  }
 
 
  public void actionPerformed(ActionEvent evt){
    String cmd = evt.getActionCommand();
    if(cmd.equalsIgnoreCase("calc")){  // calc button pressed
      // Calculate the result without a SwingWorker
      // This will freeze the GUI
      String result = getXPrime(Integer.parseInt(jtext_input.getText()));
      jlabe_result.setText(result);
    }
  }
 
 
  public static void main(String[] args) {
    new Demo1().initComponents();
  }
 
 
  /**
   * The time consuming method. For this example it's not important
   * how this method works.
   * Given a number n this method calculates the n th prime
   * e.g. given n = 5 the 5th prime is 11
   * @param the n th prime to be calculated
   * @return the prime
   */
  public String getXPrime(int n){
    int primes = 1;
    int zahl = 2;
    while(primes != n){
      zahl++;
      for(int i=2;i<zahl;i++){
        if(!(zahl%i==0) &amp;&amp; i+1 == zahl){
          primes++;
          i = zahl+1;
        }
        else if(zahl%i==0){
          i = zahl+1;
        }          
      }      
    }
    return String.valueOf(zahl);
  }
}
 

 

Führt man das Programm aus und berechnet eine relativ kleine Primzahl (z.B. die 10. Primzahl) so fällt nicht auf, dass die Berechnung im EDT läuft. Berechnet man jedoch beispielsweise die 10000. Primzahl so "friert" die GUI kurzzeitig ein. Das macht sich dadurch bemerkbar, dass der Knopf eingedrückt bleibt und auch keine Werte ins Textfeld eingegeben werden können.


Um das zu verhindern, kann der SwingWorker verwendet werden. Der folgende Quelltext erzeugt exakt das gleiche Programm. Einizger Unterschied ist, dass in der actionPerformed() Methode ein SwingWorker integriert wurde der dafür sorgt, dass die Berechnung nicht im EDT läuft. Sobald das Ergebnis der Berechnung vorliegt, führt der SwingWorker die done() Methode aus und das Ergebnis wird angezeigt.

(Download: Demo1WithSwingWorker.java):

 
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.concurrent.ExecutionException;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.SwingWorker;
 
public class Demo1WithSwingWorker extends JFrame implements ActionListener{
  JLabel jlabe_text,jlabe_result;
  JTextField jtext_input;
  JButton jbutt_calc;
  SwingWorker<String, Void> worker;
  String theresult;
 
 
  /**
   * Init the GUI elements
   */
  public void initComponents(){      
    jlabe_text = new JLabel("Calculate prime:");
    jtext_input = new JTextField();
    jlabe_result = new JLabel();
    jbutt_calc = new JButton("Calc");
    jbutt_calc.addActionListener(this);
    jbutt_calc.setActionCommand("calc");
 
    getContentPane().setLayout(new GridLayout(1,4));
    getContentPane().add(jlabe_text);
    getContentPane().add(jtext_input);
    getContentPane().add(jbutt_calc);
    getContentPane().add(jlabe_result);
 
    pack();
    setSize(400,60);
    setVisible(true);
  }
 
 
  public void actionPerformed(ActionEvent evt){
    String cmd = evt.getActionCommand();
    if(cmd.equalsIgnoreCase("calc")){  // calc button pressed
      // Construct a new SwingWorker
      worker = new SwingWorker<String, Void>(){
 
        @Override
        protected String doInBackground(){
          return getXPrime(Integer.parseInt(jtext_input.getText()));
        }
 
        @Override
        protected void done(){
          try {
            jlabe_result.setText(get());
          } catch (InterruptedException e) {
            e.printStackTrace();
          } catch (ExecutionException e) {
            e.printStackTrace();
          }
        }
      };
      // Execute the SwingWorker; the GUI will not freeze
      worker.execute();
    }
  }
 
 
  public static void main(String[] args) {
    new Demo1WithSwingWorker().initComponents();
  }
 
 
  /**
   * The time consuming method. For this example it's not important
   * how this method works.
   * Given a number n this method calculates the n th prime
   * e.g. given n = 5 the 5th prime is 11
   * @param the n th prime to be calculated
   * @return the prime
   */
  public String getXPrime(int n){
    int primes = 1;
    int zahl = 2;
    while(primes != n){
      zahl++;
      for(int i=2;i<zahl;i++){
        if(!(zahl%i==0) &amp;&amp; i+1 == zahl){
          primes++;
          i = zahl+1;
        }
        else if(zahl%i==0){
          i = zahl+1;
        }          
      }      
    }
    return String.valueOf(zahl);
  }
}
 

Wie man auf folgendem Screenshot sehen kann ist die GUI nach Drücken des Knopfes weiterhin nutzbar.

Das wars!

 
Comments (11)
11 Monday, 15 August 2011 12:33
Christian
Hallo,

ich wäre dankbar für eine erweiterung des tutorials, was den Umgang mit den PropertyChangeListenern angeht. Wie sähe das Beispiel aus, wenn ich noch eine JProgressBar mit einbauen will?

JProgressBar sollte bei N=20 beispielsweise bei der ersten gefundenen zahl auf 5% springen, sobald 10 Zahlen gefunden wurden entsprechend auf 50% etc.

Danke und Gruß
Chris
10 Monday, 20 June 2011 22:32
.
Danke für das Tutorial!

Lesbarkeit erhöhen:
Firebug -> CSS "template_css.css":
body {
background-color: #FFFFFF;
color: #000000;
font-family: Times New Roman;
font-size: 140;
}

;)
9 Tuesday, 24 May 2011 08:50
Johannes
Gutes Beispiel, danke dafür. Nur der Kontrast auf der Seite zwischen Text und Hintergrund ist imo zu gering. Gruß
8 Friday, 18 March 2011 13:51
Sascha
Knappes und verständliches Tutorial. Besser als die anderen, die ich bisher über SwingWorker im Internet finden konnte.
Vielen Dank dafür!
7 Tuesday, 20 April 2010 07:04
domme
Vielen Dank! =)
6 Tuesday, 13 April 2010 13:28
Info 08
Hallo xD
5 Tuesday, 26 January 2010 13:15
hans
ja ich schliesse mich an.
4 Friday, 11 December 2009 17:23
ok
Danke! Gutes, klares Beispiel - besser erklärt als in der "Javainsel"
3 Wednesday, 17 June 2009 10:22
cs
Gutes Beispiel und leicht zu verstehen. :)

Vielen Dank.
2 Wednesday, 02 July 2008 15:54
Shady
Schönes Beispiel,
nach langem suchen endlich eins gefunden, dass man sofort verstehen kann. Danke, saubere Arbeit!
1 Monday, 30 June 2008 08:08
ccc
cccc

Add your comment

Your name:
Comment:
  The word for verification. Lowercase letters only with no spaces.
Word verification: