1

我有这个java代码使用线程来计算一旦点击开始按钮直到没有点击停止按钮所经过的时间。

我只想使用线程来做到这一点

import javax.swing.*;
import java.awt.event.*;

// This will count the elapsed time between running time of two threads.
class ThreadGame {

    JButton button;
    MyAction my_action;

    public static void main(String[] args) {
        ThreadGame tg = new ThreadGame();
    }

    public ThreadGame() {
        JFrame frame = new JFrame("Calculate time - Game");
        button = new JButton("Start");
        button.addActionListener(new MyAction());
        frame.add(button);
        frame.setSize(400, 400);
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    class MyAction implements ActionListener {

        public void actionPerformed(ActionEvent e) {
            String text = (String) e.getActionCommand();

            final Timer timer = new Timer();

            if (text.equals("Start")) {
                button.setText("Stop");

                Thread start_time = new Thread() {
                    public void run() {
                        timer.startTime();
                    }
                };

                start_time.start();
                try {
                    start_time.join();
                } catch (Exception e1) {
                }

            } else {
                Thread stop_time = new Thread() {
                    public void run() {
                        timer.stopTime();
                    }
                };

                Thread get_time = new Thread() {
                    public void run() {
                        timer.getElapsedTime();
                        System.out.println(timer.elapsed);
                    }
                };
                stop_time.start();
                try {
                    stop_time.join();
                } catch (Exception e2) {
                }

                get_time.start();

                button.setText("Start");
            }
        }
    }

    class Timer {
        public double startTime = 0.0;
        public double stopTime = 0.0;
        public boolean running = false;
        public double elapsed = 0.0;

        public void startTime() {
            this.startTime = System.nanoTime();
            this.running = true;
        }

        public void stopTime() {
            this.stopTime = System.nanoTime();
            this.running = false;
        }

        // Elasped time in seconds
        public double getElapsedTime() {
            // double elapsed;
            if (running) {
                elapsed = ((System.nanoTime() - startTime) / 1000);
            } else {
                elapsed = ((stopTime - startTime) / 1000);
            }
            return elapsed;
        }
    }
}

EDIT:我已经理解了这个问题:timer范围是问题所在。

EDIT 2:好的,看来我只能在一个线程中使用suspendand 。resume

4

5 回答 5

1

您的问题是由timer. 这应该是私有实例变量,而不是局部方法变量。此外,对线程方法的调用startTimeendTime在线程run方法中的调用并没有为您带来任何好处,因为这些调用是非常短暂的调用。但这不是这里真正的问题。

没有理由Timer在自己的线程中运行。也就是说,在不使用专门的实时操作系统的情况下,使用线程来解决测量两个事件之间持续时间的问题是完全错误的。

您可能认为您可以创建一个带有循环的线程,该循环msecThread.sleep(1). 不要这样做! 这种时机也是完全错误的。您的计算机使用抢占式多任务模型,这意味着无法保证您的线程会定期执行。也就是说,没有什么要求它Thread.sleep(1)会休眠一段maximum时间。保证是您的线程将至少休眠 1毫秒。这意味着如果您自己在软件中管理时钟,则无法确定时钟错误,并且此错误并非无关紧要。 只是不要这样做!时间应始终由您的操作系统测量,最好使用基于中断的计时器(这是System.nanoTime在大多数平台上工作的方式,如果不是所有平台的话)。

而不是使用线程,只需直接从原始线程调用您的startTime和方法。stopTime

尝试这个:

class ThreadGame {

    JButton button;
    MyAction my_action;
    private final Timer timer = new Timer();

    public static void main(String[] args) {
        ThreadGame tg = new ThreadGame();
    }

    public ThreadGame() {
        JFrame frame = new JFrame("Calculate time - Game");
        button = new JButton("Start");
        button.addActionListener(new MyAction());
        frame.add(button);
        frame.setSize(400, 400);
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    class MyAction implements ActionListener {

        public void actionPerformed(ActionEvent e) {
            String text = (String) e.getActionCommand();

            if (text.equals("Start")) {
                timer.startTime();
                button.setText("Stop");
            } else {
                timer.stopTime();
                button.setText("Start");
            }
        }
    }

    class Timer {
        public double startTime = 0.0;
        public double stopTime = 0.0;
        public boolean running = false;
        public double elapsed = 0.0;

        public void startTime() {
            this.startTime = System.nanoTime();
            this.running = true;
        }

        public void stopTime() {
            this.stopTime = System.nanoTime();
            this.running = false;
        }

        // Elasped time in seconds
        public double getElapsedTime() {
            return (this.startTime-this.stopTime)*1000000000.0;
        }
    }
}

如果您想学习如何使用线程,请尝试编写一个应用程序来解决线程非常适合的问题。 例如,编写一个小型 Swing 应用程序,让您从 Web 下载文件。下载文件时,更新 UI 中的进度条。下载应该在独立于 UI 线程的工作线程中进行,否则 UI 将在下载过程中阻塞。

另一个示例问题是编写线程光线追踪器(这里是用 C++ 编写的示例教程)。

如果您想要更简单的东西,请编写一个 Swing 时钟。在单独的线程中使用循环以定期更新 UI,但不要使用循环来管理时间。同样,不要试图在循环中保持时间,只需让操作系统保持时间,并使用循环来安排 UI 何时更新为当前操作系统时间。

于 2012-09-12T16:24:33.133 回答
1

问题是开始按钮按下启动的对象与停止按钮按下停止的对象不同,因为每次 调用该方法时都会创建该对象。TimerTimeractionPerformed(...)

Timer需要是您MyAction班级中的一个字段。您也不需要所有线程启动/加入,因为该Timer对象非常简单和快速。

真的,您可以只使用startTimeMillis长字段而不是计时器。就像是:

class MyAction implements ActionListener {
   private long startTimeMillis;
   public void actionPerformed(ActionEvent e) {
        String text = (String) e.getActionCommand();

        if (text.equals("Start")) {
            startTimeMillis = System.currentTimeMillis();
            ...
         } else {
            System.out.println(System.currentTimeMillis() - startTimeMillis);
         }
     }
 }
于 2012-09-12T16:31:25.713 回答
0

您永远不会调用getElapsedTime()该更新elapsed字段。

于 2012-09-12T16:24:01.757 回答
0

只需这样做...

-System.currentTimeMillis()线程开始时调用。

-System.currentTimeMillis()然后在线程结束时再次调用。

- Subtract以起始值结束以获得经过的时间......

//////////////已编辑////////////////////

确保method尝试操纵(即读取和写入)variable持有的System.currentTimeMillis()必须与 同步,以便根据...synchronized keyword不会发生竞争条件Brian's Law

If you are writing a variable that might next be read by another thread, or reading a variable that might have last been written by another thread, you must use synchronization, and further, both the reader and the writer must synchronize using the same monitor lock.

于 2012-09-12T16:27:51.677 回答
0

Timer当您单击按钮时,您正在创建一个新的。使计时器成为您的类变量MyAction

下面的代码应该足以获得经过的时间。

    class MyAction implements ActionListener {
        final Timer timer = new Timer();            
        public void actionPerformed(ActionEvent e) {
            String text = (String) e.getActionCommand();                
            if (text.equals("Start")) {
                button.setText("Stop");
                timer.startTime();
            } else {
                timer.stopTime();
                System.out.println(timer.elapsed);
                button.setText("Start");
            }
        }
    } 
于 2012-09-12T16:34:07.503 回答