Wie kann man ein Java-Programm eine bestimmte Aufgabe rhythmisch wiederholt ausführen lassen?

Die Klassen TimerTask und java.util.Timer ermöglichen es in Kombination, eine beliebige Programm-Routine in definierten Zeitabständen wiederholt auszuführen.

Das Beispiel zeigt zwei Klassen, von denen Zeitsteuerung die main() -Methode enthält und eine einfache GUI mit zwei JButton und einem JLabel bereitstellt. Die Buttons starten und stoppen eine einfache Routine, die im Abstand von 1,5 sec (1500ms) ein '+' auf das JLabel setzt. Die zweite Klasse Task ist von TimerTask abgeleitet und erbt eine Methode run() , in der der Vorgang ausgeführt wird.

Aufgrund ihrer Einfachheit ist neben dem Aufbau der GUI fast die gesamte Programmsteuerung innerhalb zweier anonymer Klassen in der Methode initGUI() lokalisiert. Sie wird ihrerseits im Konstruktor der GUI-Klasse aufgerufen. Die anonymen Klassen stellen die ActionListener dar, die die Zeitsteuerung starten und stoppen.
Die Startfunktion greift auf die Instanzvariable des Typs Timer zu, initialisiert diesen neu und führt auf ihm dessen Methode schedule() aus. Ihr werden drei Parameter übergeben:

Typ Funktion
Task Referenz der von TimerTask abgeleiteten Klasse
long Startzeitpunkt gemessen vom Beginn der Ausführung an
long Zeitlicher Abstand rhythmisch addiert vom Startzeitpunkt an. Wird hier ein Wert von 1500 angegeben, so bedeutet dies, dass die zweite Ausführung nach 3000ms, die vierte nach 4500ms, etc. stattfindet.1)

Das Timer -Objekt muss vorher neu gebildet worden sein, da es im ActionListener des Stop-Buttons durch cancel() beendet wird und anschließend nicht erneut gestartet werden kann.

Dem Task -Objekt wird in jedem Falle eine Referenz auf die Klasse Zeitsteuerung übergeben, um einen Zugriff auf dessen Getter- und Setter-Methoden zu ermöglichen, die wiederum die Manipulation des Label-Textes ermöglichen. Dies geschieht so, dass in Task die Referenz an eine Instanzvariable übergeben wird, über die die Methodenaufrufe in run() erfolgen.

import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Timer;
import java.util.TimerTask;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

public class Zeitsteuerung extends JFrame {

     private Timer t;
    private JLabel label;

    public Zeitsteuerung() {
        initGUI();
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.pack();
        this.setLocationRelativeTo(null);
        this.setTitle("Zeitsteuerung");
        this.setVisible(true);
    }

    private void initGUI() {
        label = new JLabel(" ");
        JButton butt1 = new JButton("Start");
        butt1.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                t = new Timer();
                t.schedule(new Task(Zeitsteuerung.this), 0, 1500);
            }
        });
        JButton butt2 = new JButton("Stop");
        butt2.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                t.cancel();
            }
        });
        this.add(label, BorderLayout.NORTH);
        JPanel panel = new JPanel(new FlowLayout());
        panel.add(butt1);
        panel.add(butt2);
        this.add(panel, BorderLayout.SOUTH);
    }
    
    public void setText(String s){
        label.setText(s);
    }
    
    public String getText(){
        return label.getText();
    }

    public static void main(String[] args) {
        new Zeitsteuerung();
    }
}

class Task extends TimerTask {
    Zeitsteuerung z;
    
    public Task(Zeitsteuerung z){
        this.z = z;
    }
    public void run() {
        z.setText(z.getText() + "+");
    }
}

1) Die Methode schedule() ist mehrfach überladen und behandelt den dritten Parameter so, dass bei zeitlicher Verzögerung eines Ausführungsdurchgangs, der Timer versucht 'aufzuholen', die nachfolgenden Ausführungen also in kürzeren Zeitabständen stattfinden können. Will man die zeitlichen Abstände konstant halten, so muss die Methode scheduleAtFixedRate() mit den gleichen Parametern verwendet werden.