Tworzenie chronometru opartego na GUI (lub stopera)

Poświęciłem trochę czasu na program pokazujący czas, który upłynął lub pozostały czas, z którego użytkownik klika przycisk startowy, podobnie jak stoper lub chronometr, który mierzy czas do momentu zatrzymania i zresetowania. Inne przykłady czasu, jaki upłynął, to czasy okrążeń w grach wyścigowych i limity czasowe, w milisekundach, w innych grach.

Mam jednak kłopoty, ponieważ mój własny stoper nie działa w takim samym tempie jak czas rzeczywisty. To trwa dłużej niż sekundę, aby mój zegar działał jedną sekundę w dół lub w górę.

Kod jest tutaj: (GUI działa idealnie; bardziej martwię się o to, jak kontrolować wartości, aby pokazać czas, który upłynął w taki sposób, że za każdą drugą sekundę, czas wyświetlany naJLabel jest o jedną sekundę mniej. Nie mogę zmodyfikować argumentu, który został przekazanyThread.sleep ponieważ sprawi, że zegar stanie się znacznie gorszy.)

import javax.swing.*;

import java.awt.event.*;
import java.awt.*;
import java.text.DecimalFormat;
import java.util.concurrent.*;

public class StopwatchGUI3 extends JFrame 
{

    private static final long serialVersionUID = 3545053785228009472L;

    // GUI Components
    private JPanel panel;
    private JLabel timeLabel;

    private JPanel buttonPanel;
    private JButton startButton;
    private JButton resetButton;
    private JButton stopButton;

    // Properties of Program.
    private byte centiseconds = 0;
    private byte seconds = 30;
    private short minutes = 0;

    private Runnable timeTask;
    private Runnable incrementTimeTask;
    private Runnable setTimeTask;
    private DecimalFormat timeFormatter;
    private boolean timerIsRunning = true;

    private ExecutorService executor = Executors.newCachedThreadPool();

    public StopwatchGUI3()
    {
        panel = new JPanel();
        panel.setLayout(new BorderLayout());

        timeLabel = new JLabel();
        timeLabel.setFont(new Font("Consolas", Font.PLAIN, 13));
        timeLabel.setHorizontalAlignment(JLabel.CENTER);
        panel.add(timeLabel);


        buttonPanel = new JPanel();
        buttonPanel.setLayout(new FlowLayout(FlowLayout.CENTER));

        startButton = new JButton("Start");
        startButton.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e)
            {
                if (!timerIsRunning)
                    timerIsRunning = true;

                executor.execute(timeTask);
            }
        });
        buttonPanel.add(startButton);

        resetButton = new JButton("Reset");
        resetButton.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e)
            {
                timerIsRunning = false;

                centiseconds = 0;
                seconds = 30;
                minutes = 0;

                timeLabel.setText(timeFormatter.format(minutes) + ":" 
                        + timeFormatter.format(seconds) + "." 
                        + timeFormatter.format(centiseconds));
            }
        });

        buttonPanel.add(resetButton);

        stopButton = new JButton("Stop");
        stopButton.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e)
            {
                timerIsRunning = false;
            }
        });

        buttonPanel.add(stopButton);


        panel.add(buttonPanel, BorderLayout.SOUTH);


        timeFormatter = new DecimalFormat("00");

        timeTask = new Runnable(){
            public void run()
            {
                while(timerIsRunning)
                {
                    executor.execute(incrementTimeTask);

                    try
                    {
                        Thread.sleep(10);
                    }
                    catch (InterruptedException ex)
                    {
                        ex.printStackTrace();
                    }
                 }
            }
        };

        incrementTimeTask = new Runnable(){
            public void run()
            {
                if (centiseconds > 0)
                    centiseconds--;
                else
                {
                    if (seconds == 0 && minutes == 0)
                        timerIsRunning = false;
                    else if (seconds > 0)
                    {
                        seconds--;
                        centiseconds = 99;
                    }
                    else if (minutes > 0)
                    {
                        minutes--;
                        seconds = 59;
                        centiseconds = 99;
                    }
                }

                executor.execute(setTimeTask);
            }
        };

        setTimeTask = new Runnable(){
            public void run()
            {
                timeLabel.setText(timeFormatter.format(minutes) + ":" 
                        + timeFormatter.format(seconds) + "." 
                        + timeFormatter.format(centiseconds));
            }
        };

        timeLabel.setText(timeFormatter.format(minutes) + ":" 
                + timeFormatter.format(seconds) + "." 
                + timeFormatter.format(centiseconds));

        add(panel);

        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
        setTitle("StopwatchGUI.java");

        pack();
        setVisible(true);
    }

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

Musi istnieć inny sposób na zsynchronizowanie timera z czasem rzeczywistym, tak jak w prawdziwym stoperze, zamiast polegać na trzech osobnych wątkach, które, jak sądzę, są zbyt liczne dla tak dużego projektu programistycznego, ale w porządku na poziomie podstawowym dla teraz. (oh, przy okazji, theDecimalFormat klasa polega na prawidłowym sformatowaniu liczb tak jak prawdziwego stopera, chociaż nie ma wartości dziesiętnych do zaokrąglenia. Dopiero do tej pory, kiedy to opublikowałem, istnieje klasa tekstowa o nazwieSimpleDateFormat.)

Innymi słowy, chcę, aby ten program był prawdziwym stoperem. Jeśli tak nie jest, to w jaki sposób tworzysz lub używasz stopera, na przykład w grach Java?

questionAnswers(2)

yourAnswerToTheQuestion