为什么我的TimerTask继续运行得越来越快?

时间:2014-10-15 22:21:08

标签: java swing timer project

该项目是制作Asciimation播放器。在大多数情况下它最终有效,但是当asciimation循环时,它会逐渐变得越来越快。谁知道为什么?

我知道代码不是最好的,可能有很多问题。我想要一些帮助,但请不要说它糟透了,根本没有帮助。

该项目今天大约需要5个小时,因此非常感谢一些快速帮助。

import java.awt.*;
import java.awt.event.*;
import java.io.FileNotFoundException;
import java.util.Scanner;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.*;

public class ProjectTwo extends JPanel implements ActionListener{   

    private static JFrame frame;
    private static JTextArea window;    
    private static JTextField input;
    private static JLabel inputHere;
    private static JButton play;
    private static JButton pause;       
    private static String fileName = "";
    private static String textLine;     
    private static boolean haveFrameSize = false;
    private static int equalCounter;
    private static int frameSize;
    private static int lineCounter = 1;
    private static int totalFrames;
    private static int counter = 1;     
    private static Timer timer = new Timer();
    private boolean run;


    //constructor creates and adds the window, label, screen, play, and pause buttons.
    ProjectTwo(){

        window = new JTextArea(20,50);
        window.setEditable(false);
        window.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12));          
        inputHere = new JLabel("Enter file name:");
        input = new JTextField(10);

        play = new JButton("Play"); 
        play.addActionListener(this);

        pause = new JButton("Pause");
        pause.addActionListener(this);

        add(window);
        add(inputHere);
        add(input);

        add(play);
        add(pause);     
    }

    //creates the JFrame 
    public static void createAndShowGUI(){

        frame = new JFrame("AsciiMation");  
        //Create and set up the window.
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);    
        frame.setVisible(true);
        frame.setResizable(false);        
        frame.add(new ProjectTwo());    

        //Display the window.
        frame.pack();
        frame.setVisible(true);
    }

    //main
    public static void main(String[] args) {

        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createAndShowGUI();            
            }
        });
    }

    //code for pressing the play and pause button
    public void actionPerformed(ActionEvent e) {
        //if the play button was pressed
        if (e.getSource() == play){
            //if there is something in the text box
            if (!input.getText().equals("")){
                run = true;
                //if the name is not the file that is already uploaded
                if(input.getText().equals(fileName)){

                }
                    fileName = input.getText();
                    getFrameSize();

                    int counter = 1;
                    //go to method that controls the animation
                    if(!input.getText().equals("File does not exist")){
                        asciimation movie = new asciimation(fileName, frameSize, totalFrames);
                        timer.schedule(new DrawAFrameTask(), 1, 50);                         

                }
              }         
            }        


        //if the pause button was pressed
        else{
             run = false;//if pause freeze the display on a frame until the Play button is clicked again
        }       
    }

    public void getFrameSize(){
        haveFrameSize = false;
        lineCounter = 1;
        frameSize = 0;
        totalFrames = 0;
        //import the file with the inputed name
        java.io.File file = new java.io.File(fileName);
        //this is to get the size of the frame before sending it to be recorded into an array
        try{
            Scanner fileLine = new Scanner(file);

            while (fileLine.hasNextLine()){
                textLine = fileLine.nextLine();
                if(textLine.equals("=====") && haveFrameSize == false){
                    frameSize = lineCounter;
                    haveFrameSize = true;
                }
                lineCounter++;

                if(textLine.equals("=====")){
                    equalCounter++;
                }       
            }
            fileLine.close();

            totalFrames = equalCounter;

        }
        catch(FileNotFoundException f){
            input.setText("File does not exist");   
        }
    }

    private class DrawAFrameTask extends TimerTask {

        public void run() {         
            asciimation movie = new asciimation(fileName, frameSize, totalFrames);      
            window.setText(movie.displayFrame(counter));
            counter++;
            timer.schedule(new DrawAFrameTask(), 1000, 1000);
            if(counter > totalFrames){
                counter = 1;
            }
        }

    }
}












    import java.io.FileNotFoundException;
import java.util.Scanner;


public class asciimation {
    private String[] movie;
    private String textLine;
    private String line;
    private String newLine = "\n";

    int frameSize = 0;


    public asciimation(String fileName, int frameSize, int totalFrames){

        movie = new String[totalFrames];
        for(int index = 0; index < totalFrames; index++){
            movie[index] = "";
        }

        //import the file with the inputed name
                java.io.File file = new java.io.File(fileName);
                //this is to get the size of the frame before sending it to be recorded into an array

                try {
                    Scanner fileLine = new Scanner(file);

                    //while (fileLine.hasNextLine()){
                        //put each frame in the array
                        for (int array = 0; array < totalFrames-1; array++){                                                                            
                            //add each line of the frame and a /n to make the frame
                            for (int lineNum = 1; lineNum < frameSize; lineNum++){
                                if(fileLine.hasNextLine()){
                                    line = fileLine.nextLine();                                 
                                    movie[array] += ((line == null) ? "" : line) + newLine;                                 
                                }                                   
                            }
                            //skip lines that are "====="
                            if(fileLine.hasNextLine()){
                                line = fileLine.nextLine();
                            }
                        }                           
                    //}                                                 
                    fileLine.close();
                }
                catch (FileNotFoundException f){                        
                }
    }


    public String displayFrame(int frameNum){
        String frame = movie[frameNum-1];

        return frame;
    }
}

2 个答案:

答案 0 :(得分:2)

首先,如果你看一下JavaDocs for java.util.Timer,你会发现它会说

  

在指定的延迟后开始,为重复固定延迟执行安排指定的任务

所以这基本上意味着,你正在创建多次,所有都是以一定间隔重复所有这些都在更新UI的状态......

导致... Swing是一个单线程环境,意味着你永远不应该阻止具有长时间运行或阻塞进程的事件调度线程,但更重要的是,Swing不是线程安全的,这意味着你永远不应该从任何线程更新UI,然后更新事件调度线程。

因此,基于您的示例,您违反了Swing的第二条规则 - 请勿在EDT上下文之外修改UI。

请参阅Concurrency in Swing更多详情......

现在可以肯定,您可以使用SwingUtilities.invokeLater尝试解决问题,但解决这两个问题的更简单的解决方案是使用javax.swing.Timer代替java.util.Timer

有关详细信息,请参阅How to use Swing Timers

例如......

import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.FileNotFoundException;
import java.util.Scanner;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.Timer;

public class ProjectTwo extends JPanel implements ActionListener {

    private JTextArea window;
    private final JTextField input;
    private final JLabel inputHere;
    private final JButton play;
    private final JButton pause;
    private String fileName = "";
    private String textLine;
    private boolean haveFrameSize = false;
    private int equalCounter;
    private int frameSize;
    private int lineCounter = 1;
    private int totalFrames;
    private int counter = 1;
    private final Timer timer;
    private boolean run;

    private Asciimation movie;

    //constructor creates and adds the window, label, screen, play, and pause buttons.
    ProjectTwo() {

        window = new JTextArea(20, 50);
        window.setEditable(false);
        window.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12));
        inputHere = new JLabel("Enter file name:");
        input = new JTextField(10);

        play = new JButton("Play");
        play.addActionListener(this);

        pause = new JButton("Pause");
        pause.addActionListener(this);

        add(window);
        add(inputHere);
        add(input);

        add(play);
        add(pause);

        timer = new Timer(1000, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (movie != null) {
                    window.setText(movie.displayFrame(counter));
                    counter++;
                    if (counter > movie.frameSize) {
                        counter = 0;
                    }
                } else {
                    ((Timer)e.getSource()).stop();
                }
            }
        });
    }

    //main
    public static void main(String[] args) {

        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {

                JFrame frame = new JFrame("AsciiMation");
                //Create and set up the window.
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new ProjectTwo());

                //Display the window.
                frame.pack();
                frame.setVisible(true);

            }
        });
    }

    //code for pressing the play and pause button
    @Override
    public void actionPerformed(ActionEvent e) {
        //if the play button was pressed
        if (e.getSource() == play) {
            //if there is something in the text box
            if (!input.getText().equals("")) {
                run = true;
                //if the name is not the file that is already uploaded
                if (input.getText().equals(fileName)) {

                }
                fileName = input.getText();
                getFrameSize();

                int counter = 0;
                //go to method that controls the animation
                if (!input.getText().equals("File does not exist")) {
                    movie = new Asciimation(fileName, frameSize, totalFrames);
                    timer.start();
                }
            }
        } //if the pause button was pressed
        else {
            run = false;//if pause freeze the display on a frame until the Play button is clicked again
            timer.stop();
        }
    }

    public void getFrameSize() {
        haveFrameSize = false;
        lineCounter = 1;
        frameSize = 0;
        totalFrames = 0;
        //import the file with the inputed name
        java.io.File file = new java.io.File(fileName);
        //this is to get the size of the frame before sending it to be recorded into an array
        try {
            Scanner fileLine = new Scanner(file);

            while (fileLine.hasNextLine()) {
                textLine = fileLine.nextLine();
                if (textLine.equals("=====") && haveFrameSize == false) {
                    frameSize = lineCounter;
                    haveFrameSize = true;
                }
                lineCounter++;

                if (textLine.equals("=====")) {
                    equalCounter++;
                }
            }
            fileLine.close();

            totalFrames = equalCounter;

        } catch (FileNotFoundException f) {
            input.setText("File does not exist");
        }
    }

    public class Asciimation {

        private String[] movie;
        private String textLine;
        private String line;
        private String newLine = "\n";

        int frameSize = 0;

        public Asciimation(String fileName, int frameSize, int totalFrames) {

            movie = new String[totalFrames];
            for (int index = 0; index < totalFrames; index++) {
                movie[index] = "";
            }

            //import the file with the inputed name
            java.io.File file = new java.io.File(fileName);
            //this is to get the size of the frame before sending it to be recorded into an array

            try {
                Scanner fileLine = new Scanner(file);

            //while (fileLine.hasNextLine()){
                //put each frame in the array
                for (int array = 0; array < totalFrames - 1; array++) {
                    //add each line of the frame and a /n to make the frame
                    for (int lineNum = 1; lineNum < frameSize; lineNum++) {
                        if (fileLine.hasNextLine()) {
                            line = fileLine.nextLine();
                            movie[array] += ((line == null) ? "" : line) + newLine;
                        }
                    }
                    //skip lines that are "====="
                    if (fileLine.hasNextLine()) {
                        line = fileLine.nextLine();
                    }
                }
                //}                                                 
                fileLine.close();
            } catch (FileNotFoundException f) {
            }
        }

        public String displayFrame(int frameNum) {
            String frame = movie[frameNum - 1];

            return frame;
        }
    }

}

答案 1 :(得分:-1)

我缩短了你的代码。请看下面。 这部分代码是您需要了解的。 你调用timer.schedule(new Test(),1,50),这意味着你要反复每50毫秒调用一次Test.run。所以,会发生的事情是每50毫秒调用一次run(),并且每当执行run()时都会创建新的计时器。这就是为什么你的计时器变得更快(实际上个别计时器的周期是固定的,但每当执行run()时你会有越来越多的计时器)

import java.util.*;
public class Test extends TimerTask {

    private static Timer timer = new Timer();

    public static void main(String[] args) {
        Test t = new Test();
        timer.schedule(new Test(), 1, 50); 
    }
    public void run() {         

        timer.schedule(new Test(), 1000, 1000);
        System.out.println(System.currentTimeMillis()/1000);
    }

}

您可以简单地删除第二个timer.schedule()来解决问题。请参阅下文。

import java.util.*;
public class Test extends TimerTask {

    private static Timer timer = new Timer();

    public static void main(String[] args) {
        Test t = new Test();
        timer.schedule(new Test(), 1, 50); 
    }
    public void run() {         

        //timer.schedule(new Test(), 1000, 1000);
        System.out.println(System.currentTimeMillis()/1000);
    }

}