Amit Yadav
Amit Yadav

Reputation: 1861

To calculate the total time elapsed using threads in java

I have this java code using Threads to calculate the time elapsed once the start button is hit till the stop button is not hit.

I want to do this using Threads only

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: I have understand the problem: timer scope was the problem.

EDIT 2: Ok, it looks like I have to use suspend and resume in one thread only.

Upvotes: 1

Views: 7154

Answers (5)

Ben Burns
Ben Burns

Reputation: 15206

Your problem is caused by the scope of timer. This should be a private instance variable, not a local method variable. Further, wrapping calls to startTime and endTime in a thread's run method isn't gaining you anything, because these are incredibly short-lived calls. But that's not the real problem here.

There's no reason to be running Timer in its own thread. That is, without using a specialized real-time operating system, using threads to solve the problem of measuring the duration between two events is just plain wrong.

You might think that you could create a thread with a loop that increments a msec variable after a Thread.sleep(1). Don't do this! This kind of timing is also just plain wrong. Your computer uses a preemptive multitasking model which means there's no guarantee that your thread will execute on a regular interval. That is, there is nothing requiring that Thread.sleep(1) will sleep for some maximum duration. The guarantee is that your thread will sleep for a minimum of 1ms. This means there's no way to determine clock error if you're managing a clock yourself in software, and this error is not insignificant. Just don't do it!! Time should always be measured by your operating system, preferably using an interrupt-based timer (which is how System.nanoTime works on most, if not all platforms).

Instead of using a thread, just call your startTime and stopTime methods directly from your original thread.

Try this:

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

If you want to learn how to use threads, try writing an application that solves a problem for which threads are a good fit. For example, write a small Swing application that lets you download a file from the web. As the file is downloading, update a progress bar in your UI. The download should happen in a worker thread separately from the UI thread, otherwise the UI will block during the download's progress.

Another example problem is to write a threaded ray tracer (here's an example tutorial written in C++).

If you want something simpler, write a Swing clock. Use a loop within a separate thread to update the UI at a periodic interval, but do not use the loop to manage what time it is. Again, don't try to keep time in the loop, just let the OS keep the time, and use the loop to schedule when the UI gets updated with the current OS time.

Upvotes: 1

Gray
Gray

Reputation: 116878

The problem is that the start button press is starting a different Timer object than the stop-button button press is stopping because the Timer object is created every time when the actionPerformed(...) method is called.

The Timer needs to be a field in your MyAction class. You also don't need all of the thread start/joins because the Timer object is very simple and fast.

Really, you can just use a startTimeMillis long field instead of a timer. Something like:

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

Upvotes: 1

Kumar Vivek Mitra
Kumar Vivek Mitra

Reputation: 33534

Simply do this...

- Call System.currentTimeMillis() at the Starting of threads.

- Then again call System.currentTimeMillis() at the end of threads.

- Subtract the end with starting value to get the Elapsed time...

/////////////////EDITED///////////////////////

Make sure that the method trying to manipulate(ie. read and write) the variable holding the System.currentTimeMillis() must be synchronized, with synchronized keyword, so that Race Condition doesn't occur according to 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.

Upvotes: 0

basiljames
basiljames

Reputation: 4847

You are creating a new Timer when ever you click the button. Make timer a class variable of your MyAction

The code below should be sufficient to get elapsed time.

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

Upvotes: 0

Maciej
Maciej

Reputation: 598

You're never calling getElapsedTime() that updates elapsed field.

Upvotes: 0

Related Questions