Search code examples
javamultithreadingtimermilliseconds

Use of a thread messes up my pausing system


There is something wrong with my code. The timer overall seems to be working fine and the pause button does its job.

The problem is when you pause the clock at a specific time and then you unpause it.

If we (let's say) pause it at 8 seconds and we unpause it after a minute, it doesn't keep going like 9-10-11, etc. It goes 74-75-76... (I've broken it into minutes and seconds).

Is it a thread that causes the problem? (Also, I've overused freeze_sec and freeze_min time code snippets just to see if it would be fixed but it wasn't.)

Here is the code:

Thread t1 = null;

ss = new ServerSocket(6800);
while(true) {
    s = ss.accept();
    isr = new InputStreamReader(s.getInputStream());
    br = new BufferedReader(isr);
    message = br.readLine();

    if (message.equals("START")) {
        t1 = new Thread(new Thread1());
        t1.start();

        ...

    } else if (message.equals("PAUSE")) {
        if(check) {
            try {
                check = false;
                Thread1.PAUSE(true);
            } catch (Exception e) {
            System.out.println("Exception e");
        }
    } else {
        check = true;
        Thread1.PAUSE(false);
    }
}

And Thread1 class looks like:

import java.io.*;
import java.util.Date;
import java.util.Scanner;

public class Thread1 extends MyServerFrame implements Runnable{

    private static int current_min_time = 0;
    private static int current_sec_time = 0;
    private static int freeze_min_time = 0;
    private static int freeze_sec_time = 0;
    private static boolean pause = false;
    private static int minutes = 0;
    private int total_time_sec = 0;
    private static boolean freeze_signal = false;
    private static int k = 0;

    @Override
    public void run() {

        long elapsedTime = 0L;

        boolean bool = true;
        int num = 0;

        while (bool) {
            Scanner scan = new Scanner(System.in);

            if (minutes == 0) {
                System.out.println("How many minutes for this half-time?");
                Scanner in = new Scanner(System.in);
                num = in.nextInt();
                minutes = num;
            }

            long startTime = System.currentTimeMillis();

            while (total_time_sec < minutes * 60 || freeze_signal == false) {

                if (freeze_signal && k == 0) {
                    freeze_sec_time = current_sec_time;
                    freeze_min_time = current_min_time;
                    k++;
                }

                if (!pause) {
                    //perform db poll/check
                    if (elapsedTime / 1000 != current_sec_time) {
                        try {
                            clearTheFile("Half_Time.txt");
                        } catch (IOException e) {
                            System.out.println("Exception");
                        }
                        if (!freeze_signal && k > 0) {
                            current_sec_time = freeze_sec_time;
                            current_min_time = freeze_min_time;
                            k = 0;
                        }
                        current_sec_time++;
                        total_time_sec = current_sec_time + current_min_time / 60;
                        print_in_txt();
                    }
                    elapsedTime = (new Date()).getTime() - startTime;

                    if (current_sec_time == 60) {
                        if (!freeze_signal && k > 0) {
                            current_sec_time = freeze_sec_time;
                            current_min_time = freeze_min_time;
                            try {
                                clearTheFile("Half_Time.txt");
                            } catch (IOException e) {
                                System.out.println("Exception");
                            }
                            print_in_txt();
                            k = 0;
                        }
                        current_sec_time = 0;
                        current_min_time++;
                        total_time_sec = current_sec_time + current_min_time / 60;
                        startTime = System.currentTimeMillis();
                        elapsedTime = (new Date()).getTime() - startTime;
                        try {
                            clearTheFile("Half_Time.txt");
                        } catch (IOException e) {
                            System.out.println("Exception");
                        }
                        print_in_txt();
                    }
                }
            }
        }
    }        

    public static void clearTheFile(String txt_name) throws IOException {
        try {
            FileWriter fwOb = new FileWriter(txt_name, false);
            PrintWriter pwOb = new PrintWriter(fwOb, false);
            pwOb.flush();
            pwOb.close();
            fwOb.close();
        } catch (IOException e) {}
    }

    public static void print_in_txt() {
        PrintWriter out;
        try {
            out = new PrintWriter("Half_Time.txt");
            out.println(String.format("%02d", current_min_time) + ":" + String.format("%02d", current_sec_time));
            out.print("");
            out.close();
        } catch (FileNotFoundException e) {
            System.err.println("File doesn't exist");
            e.printStackTrace();
        }

    }

    public static void PAUSE(boolean p) {
        if (p) {
            pause = true;
            freeze_signal = true;
        } else {
            current_sec_time = freeze_sec_time;
            current_min_time = freeze_min_time;
            try {
                clearTheFile("Half_Time.txt");
            } catch (IOException e) {
                System.out.println("Exception");
            }
            print_in_txt();
            pause = false;
            freeze_signal = false;
        }
    }
}   


Solution

  • So, after spending some time bagging my head against the idea, I suddenly realised that you don't actually need the thread at all.

    What you need is a way to calculate the duration between two points in time, which doesn't need a thread to update the state, it's done automatically.

    The thread is just doing "other stuff"

    So, based on that, I took a StopWatch class from one of my previous answers...

    public class StopWatch {
    
        private Instant startTime;
        private Duration totalRunTime = Duration.ZERO;
    
        public StopWatch start() {
            startTime = Instant.now();
            return this;
        }
    
        public StopWatch stop() {
            Duration runTime = Duration.between(startTime, Instant.now());
            totalRunTime = totalRunTime.plus(runTime);
            startTime = null;
            return this;
        }
    
        public StopWatch pause() {
            return stop();
        }
    
        public StopWatch resume() {
            return start();
        }
    
        public StopWatch reset() {
            stop();
            totalRunTime = Duration.ZERO;
            return this;
        }
    
        public boolean isRunning() {
            return startTime != null;
        }
    
        public Duration getDuration() {
            Duration currentDuration = Duration.ZERO;
            currentDuration = currentDuration.plus(totalRunTime);
            if (isRunning()) {
                Duration runTime = Duration.between(startTime, Instant.now());
                currentDuration = currentDuration.plus(runTime);
            }
            return currentDuration;
        }
    }
    

    And applied so it could be used within a Thread, which would simply print the running time.

    Around this, I added the ability to pause, resume and stop the thread so as to demonstrate the basic idea...

    public class StopWatchRunnable implements Runnable {
    
        private final Lock pauseLock = new ReentrantLock();
        private final Condition pauseCondtion = pauseLock.newCondition();
        private final AtomicBoolean isPaused = new AtomicBoolean(false);
        private final AtomicBoolean isRunning = new AtomicBoolean(true);
    
        private final StopWatch stopWatch = new StopWatch();
    
        @Override
        public void run() {
            stopWatch.start();
            while (isRunning.get()) {
                while (isPaused.get()) {
                    pauseLock.lock();
                    stopWatch.pause();
                    try {
                        pauseCondtion.await();
                    } catch (InterruptedException ex) {
                    } finally {
                        pauseLock.unlock();
                        stopWatch.resume();
                    }
                }
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException ex) {
                }
                Duration duration = stopWatch.getDuration();
                String formatted = String.format("%dhrs %02dmins, %02dseconds", duration.toHours(), duration.toMinutesPart(), duration.toSecondsPart());
                System.out.println(formatted);
            }
        }
    
        public void stop() {
            pauseLock.lock();
            try {
                isPaused.set(false);
                isRunning.set(false);
            } finally {
                pauseCondtion.signalAll();
                pauseLock.unlock();
            }
        }
    
        public void pause() {
            pauseLock.lock();
            try {
                isPaused.set(true);
            } finally {
                pauseLock.unlock();
            }
        }
    
        public void resume() {
            pauseLock.lock();
            try {
                isPaused.set(false);
            } finally {
                pauseCondtion.signalAll();
                pauseLock.unlock();
            }
        }
    
    }
    

    Runnable example...

    This basically takes the code from above and dumps it into a simple runnable example which demonstrates the pause/resume functionality

    import java.time.Duration;
    import java.time.Instant;
    import java.util.concurrent.atomic.AtomicBoolean;
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class StopWatchExample {
    
        public static void main(String[] args) throws InterruptedException {
            new StopWatchExample();
        }
    
        public StopWatchExample() throws InterruptedException {
            StopWatchRunnable stopWatch = new StopWatchRunnable();
            Thread thread = new Thread(stopWatch);
            thread.start();
    
            Thread.sleep(5000);
            System.out.println("Pause...");
            stopWatch.pause();
            Thread.sleep(5000);
            System.out.println("Resume...");
            stopWatch.resume();
            Thread.sleep(5000);
            System.out.println("Stop...");
            stopWatch.stop();
            thread.join();
            System.out.println("All done...");
        }
    
        public class StopWatch {
    
            private Instant startTime;
            private Duration totalRunTime = Duration.ZERO;
    
            public StopWatch start() {
                startTime = Instant.now();
                return this;
            }
    
            public StopWatch stop() {
                Duration runTime = Duration.between(startTime, Instant.now());
                totalRunTime = totalRunTime.plus(runTime);
                startTime = null;
                return this;
            }
    
            public StopWatch pause() {
                return stop();
            }
    
            public StopWatch resume() {
                return start();
            }
    
            public StopWatch reset() {
                stop();
                totalRunTime = Duration.ZERO;
                return this;
            }
    
            public boolean isRunning() {
                return startTime != null;
            }
    
            public Duration getDuration() {
                Duration currentDuration = Duration.ZERO;
                currentDuration = currentDuration.plus(totalRunTime);
                if (isRunning()) {
                    Duration runTime = Duration.between(startTime, Instant.now());
                    currentDuration = currentDuration.plus(runTime);
                }
                return currentDuration;
            }
        }
    
        public class StopWatchRunnable implements Runnable {
    
            private final Lock pauseLock = new ReentrantLock();
            private final Condition pauseCondtion = pauseLock.newCondition();
            private final AtomicBoolean isPaused = new AtomicBoolean(false);
            private final AtomicBoolean isRunning = new AtomicBoolean(true);
    
            private final StopWatch stopWatch = new StopWatch();
    
            @Override
            public void run() {
                stopWatch.start();
                while (isRunning.get()) {
                    while (isPaused.get()) {
                        pauseLock.lock();
                        stopWatch.pause();
                        try {
                            pauseCondtion.await();
                        } catch (InterruptedException ex) {
                        } finally {
                            pauseLock.unlock();
                            stopWatch.resume();
                        }
                    }
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException ex) {
                    }
                    Duration duration = stopWatch.getDuration();
                    String formatted = String.format("%dhrs %02dmins, %02dseconds", duration.toHours(), duration.toMinutesPart(), duration.toSecondsPart());
                    System.out.println(formatted);
                }
            }
    
            public void stop() {
                pauseLock.lock();
                try {
                    isPaused.set(false);
                    isRunning.set(false);
                } finally {
                    pauseCondtion.signalAll();
                    pauseLock.unlock();
                }
            }
    
            public void pause() {
                pauseLock.lock();
                try {
                    isPaused.set(true);
                } finally {
                    pauseLock.unlock();
                }
            }
    
            public void resume() {
                pauseLock.lock();
                try {
                    isPaused.set(false);
                } finally {
                    pauseCondtion.signalAll();
                    pauseLock.unlock();
                }
            }
    
        }
    
    }