Danny P. Danny P. - 4 months ago 17
Java Question

TimerTask stop all other execution

I have a program that starts a timer and then repeats code every second, before it stops. What happens is it does not stop the execution coming from main. I am assuming it is only one thread that is running along with it, but from looking at posts I am still not sure how to temporarily halt that.

final class DogDoor{
private static DogDoor dogDoor;
private boolean open;

private DogDoor(){
open=false;
}

public static synchronized DogDoor getDogDoor(){
if(dogDoor==null){
dogDoor=new DogDoor();
}
return dogDoor;
}

public void activate(){
open=!open;
System.out.println(open? "Door is open!": "Door is closed!");
if(open){
autoClose();
}
}
public void autoClose(){
System.out.print("Door closing in 5 seconds . . .");

//right before it starts the timer and task
timer();

//then resume here after the timer completes
activate();
}
public void timer(){
new java.util.Timer().scheduleAtFixedRate(
new java.util.TimerTask(){

//Prefer to stop until this completes
int s=5;
@Override
public void run(){
System.out.print(" .");
if((s--)==0){
cancel();
}
}
}, 0, 1000);
}
}

final class Remote{
private static Remote remote;
private boolean windowsLocked;
private int lockCode;

public Remote(){
windowsLocked=true;
generateLockCode();
}

public static synchronized Remote getRemote(){
if(remote==null){
remote=new Remote();
}
return remote;
}
public String getLockCode(){
return String.format("%03d", lockCode);
}

public boolean windowsLocked(){
return windowsLocked;
}
public void generateLockCode(){
lockCode=(int)(Math.random()*1000);
}
public void toggleDoor(){
DogDoor.getDogDoor().activate();
}
public void fixWindows(){
if(passCodeVerifier()){
windowsLocked=!windowsLocked;
System.out.println(windowsLocked? "Windows locked!": "Windows unlocked!");
}
else{
System.out.println("Invalid passcode!");
}
}
public boolean passCodeVerifier(){
Scanner scanner=new Scanner(System.in);
System.out.print("Enter three digit keypad code: ");
return scanner.hasNext("^\\d{3}$") && Integer.parseInt(scanner.next())==(lockCode);
}
}

public class DogDoorDemo{
public static void main(String[] args){
System.out.println("Dog door demo.");
System.out.println();

System.out.println(Remote.getRemote().getLockCode());
Remote.getRemote().toggleDoor();

//and finally resume here
Remote.getRemote().fixWindows();
}
}

Answer

As mentioned by the documentation, Timers are:

A facility for threads to schedule tasks for future execution in a **background thread**

This means any part of the program run using a TimerTask will be run on a separate thread from the main one, and therefore will not block execution on the main thread.

An alternative solution would be to sleep the thread for the desired amount of seconds as follows (replace your timer() method with this one and catch the InterruptedException.):

void timer(int seconds)throws InterruptedException{
    for(int i = 0; i < seconds; i++){
        Thread.sleep(1000);
        //Output whatever message you want to appear every second. The variable 'i' will hold the current second in the countdown.
    }
}

And replace your autoClose method with this:

public void autoClose(){
    System.out.print("Door closing in 5 seconds . . .");

    //right before it starts the timer and task
    timer(5);

    //then resume here after the timer completes
    activate();
}

Thread#sleep throws an InterruptedException so this must be handled appropriately.