Wielokrotne zegary powodują wiele działań

Robię projekt Java, w którym muszę stworzyć GUI, który zawiera wiele timerów, które odliczają czas wprowadzania danych przez użytkownika. Kiedy tworzę tylko jeden timer, program działa dobrze, ale gdy próbuję włączyć wiele timerów, każdy timer będzie odliczał szybciej w zależności od tego, ile jest timerów. Myślę, że problem jest wywoływany przez wielokrotne wywoływanie ActionListener za każdym razem, gdy huśtawka wywołuje akcję, ale nie wiem, jak rozwiązać ten problem.

Oto moja klasa CountDownTimer, która zawiera atrybuty i metody zegara:

package pack1;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Scanner;

/*******************************************************************************
 * Class that contains the methods needed to create a timer that counts down
 * @version January 2014, Project 1
 ********************************************************************************/
public class CountDownTimer {
    /** Amount of hours that are being counted down in the timer */
    int hours;
    /** Amount of minutes that are being counted down in the timer */
    int minutes;
    /** Amount of seconds that are being counted down in the timer */
    int seconds;

    /** Boolean that determines if the add method will work. It is changed with the toggleSuspend method */
    static boolean toggle = false;

    /*****************************************************************************************
     * Default constructor that creates a CountDownTimer object with no time contained in it
     *****************************************************************************************/
    public CountDownTimer() {
        hours = 0;
        minutes = 0;
        seconds = 0;
    }

    /*********************************************************************************************
     * Constructor that uses the input amount of hours, minutes, and seconds to count down from
     * 
     * Does not allow time that is negative or allow seconds or minutes that are over 60
     * @param hours amount of hours that will be counted down from
     * @param minutes amount of minutes that will be counted down from
     * @param seconds amount of seconds that will be counted down from
     *********************************************************************************************/
    public CountDownTimer(int hours, int minutes, int seconds) {
        super();

        // doesn't allow negative minutes, seconds, or hours
        if (seconds < 0 || minutes < 0 || hours < 0) {
            throw new IllegalArgumentException("Time cannot be negative");
        }
        // doesn't allow seconds that are higher than 60
        if (seconds >= 60) {
            throw new IllegalArgumentException(
                    "Cannot have more than 60 seconds");
        }
        // doesn't allow minutes that are higher than 60
        if (minutes >= 60) {
            throw new IllegalArgumentException(
                    "Cannot have more than 60 minutes");
        }
        this.hours = hours;
        this.minutes = minutes;
        this.seconds = seconds;
    }
    /*********************************************************************
     * Constructor that takes minutes and seconds, and sets hours to zero
     * also doesn't allow minutes or seconds to be negative or above 60
     * @param minutes amount of minutes that will be counted down from
     * @param seconds amount of seconds that will be counted down from
     *************************************************************************/
    public CountDownTimer(int minutes, int seconds) {
        super();
        // doesn't allow seconds minutes or hours to be negative
        if (seconds < 0 || minutes < 0 || hours < 0) {
            throw new IllegalArgumentException("Time cannot be negative");
        }
        // doesn't allow seconds to be greater than 60
        if (seconds >= 60) {
            throw new IllegalArgumentException(
                    "Cannot have more than 60 seconds");
        }
        // doesn't allow minutes to be greater than 60
        if (minutes >= 60) {
            throw new IllegalArgumentException(
                    "Cannot have more than 60 minutes");
        }

        this.hours = 0;
        this.minutes = minutes;
        this.seconds = seconds;
    }

    /***********************************************************************
     * Constructor that only takes seconds and sets hours and minutes to 0
     * does not allow the seconds to be above 60 or negative
     * @param seconds amount of seconds that will be counted down from
     *************************************************************************/
    public CountDownTimer(int seconds) {
        super();

        // doesn't allow seconds minutes or hours to be negative
        if (seconds < 0 || minutes < 0 || hours < 0) {
            throw new IllegalArgumentException("Time cannot be negative");
        }
        // doesn't allow seconds to be greater than 60
        if (seconds >= 60) {
            throw new IllegalArgumentException(
                    "Cannot have more than 60 seconds");
        }
        this.hours = 0;
        this.minutes = 0;
        this.seconds = seconds;
    }


    /** 
     * Constructor that clones one CountDownTimer object into a new CountDownTimer object
     * @param other The CountDownTimer object that is being cloned
     * */
    public CountDownTimer(CountDownTimer other) {
        this.hours = other.hours;
        this.minutes = other.minutes;
        this.seconds = other.seconds;
    }

    /*******************************************************************************************************************************
     * Constructor that converts a string in the format of "00:00:00" into seconds minutes and hours so it can be counted down from
     * @param startTime String that is converted into seconds minutes and hours
     *******************************************************************************************************************************/
    public CountDownTimer(String startTime) {
        // Separates the seconds minutes and hours into an array
        String[] parts = startTime.split(":");
        // if the array has only one cell, that means only seconds were input
        if (parts.length == 1) {
            seconds = Integer.parseInt(parts[0]);
        }
        // if the array has only 2 cells that means there is only minutes and seconds input
        if (parts.length == 2) {
            minutes = Integer.parseInt(parts[0]);
            seconds = Integer.parseInt(parts[1]);
        }
        // if the array has 3 cells that means there is seconds minutes and hours input
        if (parts.length == 3) {
            hours = Integer.parseInt(parts[0]);
            minutes = Integer.parseInt(parts[1]);
            seconds = Integer.parseInt(parts[2]);
        }
        // doesn't allow seconds minutes or hours to be negative
        if (seconds < 0 || minutes < 0 || hours < 0) {
            throw new IllegalArgumentException("Time cannot be negative");
        }
        // doesn't allow seconds to be greater than or equal to 60
        if (seconds >= 60) {
            throw new IllegalArgumentException(
                    "Cannot have more than 60 seconds");
        }
        // doesn't allow minutes to be greater than or equal to 60
        if (minutes >= 60) {
            throw new IllegalArgumentException(
                    "Cannot have more than 60 minutes");
        }
    }

    /**************************************************************************************************
     * Method that returns true or false based on whether or not two CountDownTimer objects are equal
     * @param other Object that is being compared to another CountDownTimer
     **************************************************************************************************/
    public boolean equals(Object other) {
        // converts the two objects to seconds then compares them
        if (this.convertToSeconds() == ((CountDownTimer) other)
                .convertToSeconds())
            return true;
        return false;
    }

    /********************************************************************************
     * Returns a boolean based on whether two CountDownTimers, t1 and t2, are equal
     * @param t1 first CountDownTimer being compared
     * @param t2 second CountDownTimer being compared
     ********************************************************************************/
    public static boolean equals(CountDownTimer t1, CountDownTimer t2) {
        // converts the two objects to seconds and then compares them
        if (t1.convertToSeconds() == t2.convertToSeconds())
            return true;
        return false;
    }

    /************************************************************************************************
     * Compares to CountDownTimer objects and returns an int 1, 0, or -1 based on whether the first
     * object is greater than, equal to, or less than the CountDownTimer in the parameter
     * @param other CountDownTimer that is being compared
     *************************************************************************************************/
    public int compareTo(CountDownTimer other) {
        if (this.convertToSeconds() > other.convertToSeconds())
            return 1;
        else if (this.convertToSeconds() < other.convertToSeconds())
            return -1;
        return 0;
    }

    /**************************************************************************************************
     * Compares to CountDownTimer objects and returns an int 1, 0, or -1 based on whether the first
     * object (t1) is greater than, equal to, or less than the second object (t2)
     * @param t1 first object being compared
     * @param t2 second object being compared
     * @return
     ***************************************************************************************************/
    public static int compareTo(CountDownTimer t1, CountDownTimer t2) {
        if (t1.convertToSeconds() > t2.convertToSeconds())
            return 1;
        else if (t1.convertToSeconds() < t2.convertToSeconds())
            return -1;
        return 0;
    }

    /***************************************************************
     * subtracts the input amount of seconds from a CountDownTimer
     * @param seconds amount of seconds the user wants to subtract
     ***************************************************************/
    public void subtract(int seconds) {
        // places the amount of seconds into an integer
        int tempSeconds = this.convertToSeconds();
        // subtracts the input seconds from the seconds that were converted
        tempSeconds -= seconds;
        // converts the new seconds back into the object
        formatSeconds(tempSeconds);
    }


    /*******************************************************************************************
     * Subtracts the amount of time contained in one CountDownTimer from another CountDownTimer
     * @param other CountDownTimer that is doing the subtracting
     *******************************************************************************************/
    public void subtract(CountDownTimer other) {
        int otherSeconds = other.convertToSeconds();
        this.subtract(otherSeconds);
    }

    /***********************************************************************
     * Adds seconds to the object based on what is put into the parameter
     * @param seconds amount of seconds being added to the CountDownTimer
     ************************************************************************/
    public void add(int seconds) {
        // keeps the method from adding when the toggle is activated
        if (toggle == false) {
            int tempSeconds = this.convertToSeconds();
            tempSeconds += seconds;
            formatSeconds(tempSeconds);
        } else
            throw new IllegalArgumentException(
                    "Cannot use add when toggle is enabled");
    }
/**
 * Adds the seconds from one CountDownTimer to another CountDownTimer
 * @param other CountDownTimer that is being added to another CountDowntimer
 */
    public void add(CountDownTimer other) {
        // doesn't allow the method to add when the toggle is true
        if (toggle == false) {
            int otherSeconds = other.convertToSeconds();
            this.add(otherSeconds);
        } else
            throw new IllegalArgumentException(
                    "Cannot use add when toggle is enabled");
    }

    /*******************************************
     * Decreases the CountDownTimer by 1 second
     *******************************************/
    public void dec() {
        int tempSeconds = this.convertToSeconds();
        tempSeconds--;
        formatSeconds(tempSeconds);
    }

    /****************************************************
     * Increases the CountDownTimer object by 1 second
     ***************************************************/
    public void inc() {
        int tempSeconds = this.convertToSeconds();
        tempSeconds--;
        formatSeconds(tempSeconds);
    }

    /**
     * Returns the object as a string in the format of "00:00:00"
     */
    public String toString() {
        String time = "" + this.hours + ":";
        if (this.minutes < 10) {
            time += "0" + this.minutes + ":";
        } else {
            time += this.minutes + ":";
        }
        if (this.seconds < 10) {
            time += "0" + this.seconds;
        } else {
            time += this.seconds;
        }
        return time;
    }

    /************************************************
     * Saves the object with a specified name
     * @param fileName name of the file being saved
     *************************************************/
    public void save(String fileName) {
        PrintWriter out = null;
        try {
            out = new PrintWriter(new BufferedWriter(new FileWriter(fileName)));
        } catch (IOException e) {
            e.printStackTrace();
        }
        out.println(this.hours);
        out.println(this.minutes);
        out.println(this.seconds);
        out.close();
    }
/**************************************************
 * Loads the object with the specified name
 * @param fileName Name of the file being loaded
 **************************************************/
    public void load(String fileName) {
        try {
            Scanner fileReader = new Scanner(new File(fileName));

            this.hours = fileReader.nextInt();
            this.minutes = fileReader.nextInt();
            this.seconds = fileReader.nextInt();

            fileReader.close();
            System.out.println("Hours: " + this.hours);
            System.out.println("Minutes: " + this.minutes);
            System.out.println("Seconds: " + this.seconds);
        } catch (FileNotFoundException error) {
            System.out.println("File not found");
        } catch (IOException error) {
            System.out.println("OH NO THAT WAS NOT SUPPOSED TO HAPPEN");
        }
    }
/**********************************************************************************************
 * Switches the toggle boolean, and doesn't allow the add methods to work when it is activated
 ************************************************************************************************/
    public static void toggleSuspend() {
        if (toggle == false)
            toggle = true;
        if (toggle == true)
            toggle = false;
    }

    /***********************************************************************************
     * Formats a certain amount of seconds and puts it into an existing CountDownTimer
     * @param seconds seconds being formatted
     ***********************************************************************************/
    private void formatSeconds(int seconds) {
        this.hours = seconds / 3600;
        seconds %= 3600;
        this.minutes = seconds / 60;
        this.seconds = seconds % 60;
    }

    /*****************************************************************************
     * Returns the amount of seconds that are contained in a CountDownTime object
     *****************************************************************************/
    private int convertToSeconds() {
        int hSeconds = hours * 3600;
        int mSeconds = minutes * 60;
        return hSeconds + mSeconds + seconds;
    }
}

A oto moja klasa panelu GUI, w której mam problem. Używam tablic do tworzenia wielu timerów.

package pack1;

import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.Timer;

public class MyTimerPanel extends JPanel {
    /** Array of CountDownTimers that will be used */
    private CountDownTimer[] cdt;

    /** Array of Timer objects that are used to count down by 1 every second*/
    private Timer[] javaTimer;
    /** Array of TimerListeners that are called in the Timer objects */
    private TimerListener timer[];

    /** Array of JPanels that will hold each timer interface */
    private JPanel[] panel;
    /** Array of JLabels for the display of time, seconds, minutes, and hours */
    private JLabel[] timeDisplay, secDisplay, minDisplay, hourDisplay;
    /** Array of JButtons that start, stop, and set the timer */
    private JButton[] start, stop, set;
    /** Array of JTextFields that the user will input the seconds, minutes, and hours*/
    private JTextField[] sec, min, hour;

    /** Array of booleans that will determine if the timer should be counting down */ 
    private boolean[] go;

    /** Amount of milliseconds the timer needs to wait before performing an action */
    private final int DELAY = 1000;

    /** Amount of CountDownTimers in the interfaces */ 
    private final int AMOUNT = 3;

    // test integer
    int[] count;


    /******************************************************************************************
     * Constructor that sets the lengths of the arrays and instantiates each part of the array
     ******************************************************************************************/
    public MyTimerPanel() {
        count = new int[AMOUNT];
        timer = new TimerListener[AMOUNT];
        cdt = new CountDownTimer[AMOUNT];
        javaTimer = new Timer[AMOUNT];
        panel = new JPanel[AMOUNT];
        timeDisplay = new JLabel[AMOUNT];
        secDisplay = new JLabel[AMOUNT];
        minDisplay = new JLabel[AMOUNT];
        hourDisplay = new JLabel[AMOUNT];
        sec = new JTextField[AMOUNT];
        min = new JTextField[AMOUNT];
        hour = new JTextField[AMOUNT];
        start = new JButton[AMOUNT];
        stop = new JButton[AMOUNT];
        set = new JButton[AMOUNT];
        go = new boolean[AMOUNT];
        // Defines each part of each array and adds the neccessary components to the buttons and panels
        for (int i = 0; i < AMOUNT; i++) {
            count[i] = 0;
            cdt[i] = new CountDownTimer(0, 0, 0);
            go[i] = false;
            timer[i] = new TimerListener();
            javaTimer[i] = new Timer(DELAY, timer[i]);
            javaTimer[i].start();
            timeDisplay[i] = new JLabel(cdt[i].toString());
            secDisplay[i] = new JLabel("Seconds: ");
            minDisplay[i] = new JLabel("Minutes: ");
            hourDisplay[i] = new JLabel("Hours: ");
            sec[i] = new JTextField(5);
            min[i] = new JTextField(5);
            hour[i] = new JTextField(5);
            start[i] = new JButton("Start");
            stop[i] = new JButton("Stop");
            set[i] = new JButton("Set");
            start[i].addActionListener(timer[i]);
            stop[i].addActionListener(timer[i]);
            set[i].addActionListener(timer[i]);
            panel[i] = new JPanel();
            panel[i].add(hourDisplay[i]);
            panel[i].add(hour[i]);
            panel[i].add(minDisplay[i]);
            panel[i].add(min[i]);
            panel[i].add(secDisplay[i]);
            panel[i].add(sec[i]);
            panel[i].add(start[i]);
            panel[i].add(stop[i]);
            panel[i].add(set[i]);
            panel[i].add(timeDisplay[i]);
            add(panel[i]);
        }
        setPreferredSize(new Dimension(750, 40 * AMOUNT));
    }

    /*******************************************************************
     * Action Listener that activates when certain buttons are pressed
     *******************************************************************/
    private class TimerListener implements ActionListener {
        /********************************************************************
         * ActionListener that is activated whenever someone pushes a button
         ********************************************************************/
        public void actionPerformed(ActionEvent e) {
            // CountDownTimer that will be compared to at zero
            CountDownTimer zero = new CountDownTimer();
            // placeholders for the text from the JTextFields
            String[] secText = new String[AMOUNT];
            String[] minText = new String[AMOUNT];
            String[] hourText = new String[AMOUNT];
            // runs through each part of the arrays and checks which button needs to be pressed
            for (int i = 0; i < AMOUNT; i++) {
                // checks if one of the start buttons is being pressed
                if (e.getSource() == start[i]){
                    go[i] = true;
                }
                // checks if one of the stop buttons is being pressed
                else if (e.getSource() == stop[i])
                    go[i] = false;
                // checks if one of the set buttons is being pressed and sets the minutes hours and seconds input
                if (e.getSource() == set[i]) {
                    secText[i] = sec[i].getText();
                    minText[i] = min[i].getText();
                    hourText[i] = hour[i].getText();
                    // if one of the boxes is blank, it will input zero
                    if (secText[i].equals("")) {
                        cdt[i].seconds = 0;
                    } else {
                        cdt[i].seconds = Integer.parseInt(secText[i]);
                    }
                    if (minText[i].equals("")) {
                        cdt[i].minutes = 0;
                    } else {
                        cdt[i].minutes = Integer.parseInt(minText[i]);
                    }
                    if (hourText[i].equals("")) {
                        cdt[i].hours = 0;
                    } else {
                        cdt[i].hours = Integer.parseInt(hourText[i]);
                    }
                    // sets timeDisplay to show the time in the countdowntimer
                    timeDisplay[i].setText(cdt[i].toString());
                    // stops the timer after setting it
                    go[i] = false;
                }
                // the timer should be counting down if the go boolean is true or the countdowntimer isn't zero
                if (go[i] == true && !cdt[i].equals(zero)) {
                    javaTimer[i].start();
                    cdt[i].dec();
                    timeDisplay[i].setText(cdt[i].toString());
                    count[i]++;
                    }
                // the timer shouldn't be counting down if the go boolean is false or the countdowntimer is zero
                if (go[i] == false || cdt[i].compareTo(zero) <= 0) {
                    javaTimer[i].stop();
                }
            }
            // for testing purposes
            System.out.println(count[0] + "\t" + count[1] + "\t" + count[2]);

        }
    }
}

questionAnswers(1)

yourAnswerToTheQuestion