Несколько таймеров Swing вызывают многократные действия

Я делаю проект Java, где я должен сделать графический интерфейс, который содержит несколько таймеров, которые отсчитывают от времени ввода пользователя. Когда я создаю только один таймер, программа работает нормально, но когда я пытаюсь включить несколько таймеров, каждый таймер будет отсчитывать быстрее, основываясь на количестве таймеров. Я думаю, что проблема вызвана тем, что ActionListener вызывается несколько раз каждый раз, когда таймер колебания вызывает действие, но я не знаю, как решить эту проблему.

Вот мой класс CountDownTimer, который содержит атрибуты и методы для таймера:

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;
    }
}

А вот мой класс панели GUI, где яУ меня проблемы. Я'используя массивы для создания нескольких таймеров.

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) 

Ответы на вопрос(1)

Ваш ответ на вопрос