2

I am trying to build multiple Timers and scheduled independent tasks for each of them. I have a constructor for holding a Timer and its variable. And I would time to set different lifespan for each timer, for example one timer will be terminated after 1 day whilst keep other run forever. I have a thought to build another a timer on top of those timers and control their status but I am afraid that the program would be messy and bulky. Is there any other method for this? Thanks

Code:

public class App 
{
    public static void main( String[] args )
    {
        System.out.println( "Hello World!" );
        TimerTrigger.INSTANCE.runTimer();
    }
}

To Trigger the timer:

public enum TimerTrigger {
    INSTANCE;

    private TimerTrigger(){}

    public void runTimer(){
      for (int i = 0; i < 3; i++) {
        System.out.println( "Initiaizing Timer " + i );
        TimerConstructor tmpTimer = new TimerConstructor();
        varObjectvo timerVariable = new varObjectvo();
        timerVariable.setLifespan('24'); //24 hour
        timerVariable.setAliveTime(30); // seconds
        tmpTimer.start(timerVariable); //timerVariable is a value object
      }
    }
}

The Constructor of timer:

import java.time.LocalDateTime;
import java.util.concurrent.*;
import java.util.Date;

public class TimerConstructor{
    private static varObjectvo timerVO = null;
  
    public void start(varObjectvo obj) {
        timerVO = obj;
        executor  = Executors.newSingleThreadScheduledExecutor();
        startTime = LocalDateTime.now();
        endTime = startTime.plusSeconds(obj.getAliveTime());

        timer.scheduleAtFixedRate(task, new Date(), 10000);

        while (true) {
            if (endTime.getHour()==LocalDateTime.now().getHour() && 
                endTime.getMinute()==LocalDateTime.now().getMinute() &&
                endTime.getSecond()==LocalDateTime.now().getSecond()) {
                scheduleFuture.cancel(true);
                executor.shutdown();
                System.out.println("Cancelling " + scheduleFuture.toString());
            
                break;
            }
        }    

    }
  
    private class TimerChecker extends TimerTask {
      public void run() {
        System.out.println("It is timer " + timerVO.getIndex());
      }
    }
  }

The value object class:

public class varObjectvo{
    private Integer index;
    private Integer lifespan;
 
    public void setIndex(Integer i){ 
        this.index = i;
    }
    public Integer getIndex(){ 
        return this.index;
    }
    
    public void setLifespan(Integer i){ 
        this.lifespan= i;
    };
    public Integer getLifespan(){ 
        return this.lifespan;
    };
  }
WILLIAM
  • 457
  • 5
  • 28
  • 1
    Did you consider an [`ExecutorService`](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/ExecutorService.html)? I believe `java.util.Timer` is legacy. – Ole V.V. Jun 19 '22 at 07:14
  • 1
    See also: [*ExecutorService that interrupts tasks after a timeout*](https://stackoverflow.com/q/2758612/642706) – Basil Bourque Jun 20 '22 at 07:37

2 Answers2

3

Executors framework

I am trying to build multiple Timers

If you read the Javadoc for Timer & TimerTask, you will see notes explaining that these classes are now legacy.

Is there any other method for this?

Yes.

The Timer & TimerTask classes were long ago supplanted by the Executors framework added to Java 5. See tutorial by Oracle.

Define your task as a Runnable or a Callable. Submit an instance of your task to an ExecutorService.

scheduled independent tasks for each of them.

One executor service can handle multiple tasks.

A scheduled executor service can run each submitted task after a specified amount of time, or run repeatedly.

keep other run forever

Submit your task to a scheduled executor service to have it run repeatedly and indefinitely.

one timer will be terminated after 1 day

Make your task reschedule itself until expiration.

Write your task class such that it holds a reference to the scheduled executor service, remembers when it first started, and reschedules itself for the next run unless the specified limit of time has elapsed.

I have a thought to build another a timer on top of those timers

No need. You would be reinventing the scheduled executor service.

Search Stack Overflow to learn more. All of this has been covered multiple times already.

Example code

Here is some rough code to get you going.

Define a task that tells the current time. We will see this running once, and also see this running indefinitely.

public class TellTime implements Runnable
{

    @Override
    public void run ( )
    {
        System.out.println( "Current moment: " + Instant.now() );
    }
}

And another task to perform a countdown. We will run this for a specified time limit. Notice how an instance of this class holds a reference to the scheduled executor service, and then uses that to schedule its own next execution.

public class Countdown implements Runnable
{
    private final Duration countingDown;
    private final Duration sleep;
    private final ScheduledExecutorService scheduledExecutorService;
    private final Instant start;

    public Countdown ( Duration countingDown , Duration sleep , ScheduledExecutorService scheduledExecutorService )
    {
        this.countingDown = countingDown;
        this.sleep = sleep;
        this.scheduledExecutorService = scheduledExecutorService;
        this.start = Instant.now();
    }

    @Override
    public void run ( )
    {
        Instant end = this.start.plus( this.countingDown );
        Duration remaining = Duration.between( Instant.now() , end );
        if ( remaining.isNegative() )
        {
            Instant now = Instant.now();
            Duration elapsed = Duration.between( this.start , now );
            System.out.println( "Countdown " + this.countingDown + " expired at " + now + ". Elapsed: " + elapsed + ". " );
        }
        else
        {
            System.out.println( "Countdown remaining: " + remaining );
            this.scheduledExecutorService.schedule( this , this.sleep.toNanos() , TimeUnit.NANOSECONDS );
        }
    }
}

And an app to run these tasks.

public class App
{
    private ScheduledExecutorService ses;

    public static void main ( String[] args )
    {
        App app = new App();
        app.demo();
    }

    private void demo ( )
    {
        ScheduledExecutorService ses = Executors.newSingleThreadScheduledExecutor();

        // Run once.
        ses.schedule( new TellTime() , 5 , TimeUnit.SECONDS );

        // Run repeatedly, indefinitely.
        ses.scheduleAtFixedRate( new TellTime() , 20 , 10 , TimeUnit.SECONDS );

        // Run for a limited time.
        Countdown countdown = new Countdown( Duration.ofHours( 1 ) , Duration.ofMinutes( 1 ) , ses );
        ses.schedule( countdown , 1 , TimeUnit.MINUTES );

        // Let the app run until the user wants to stop.
        try (
                Scanner scanner = new Scanner( System.in ) ;
        )
        {
            System.out.println( "Type anything to end this app." );
            String ignoreThis = scanner.nextLine();
        }
        this.shutdownScheduledExecutorService();
    }

    private void shutdownScheduledExecutorService ( )
    {
        // Eventually shutdown your executor service.
        // Otherwise, its backing thread pool may continue indefinitely, like a zombie. ‍
        // This code is a slightly modified copy taken from the Javadoc of `ExecutorService` class in Java 17.
        if ( Objects.isNull( this.ses ) ) { return; }
        this.ses.shutdown(); // Disable new tasks from being submitted
        try
        {
            // Wait a while for existing tasks to terminate
            if ( ! this.ses.awaitTermination( 60 , TimeUnit.SECONDS ) )
            {
                this.ses.shutdownNow(); // Cancel currently executing tasks
                // Wait a while for tasks to respond to being cancelled
                if ( ! this.ses.awaitTermination( 60 , TimeUnit.SECONDS ) )
                { System.err.println( "Pool did not terminate" ); }
            }
        }
        catch ( InterruptedException ex )
        {
            // (Re-)Cancel if current thread also interrupted
            this.ses.shutdownNow();
            // Preserve interrupt status
            Thread.currentThread().interrupt();
        }
    }
}

When run.

Type anything to end this app.
Current moment: 2022-06-20T07:26:23.732838Z
Current moment: 2022-06-20T07:26:38.732350Z
Current moment: 2022-06-20T07:26:48.731164Z
Current moment: 2022-06-20T07:26:58.727703Z
Current moment: 2022-06-20T07:27:08.730252Z
Current moment: 2022-06-20T07:27:18.727893Z
Countdown remaining: PT58M59.998505S
Current moment: 2022-06-20T07:27:28.730477Z
Current moment: 2022-06-20T07:27:38.727705Z
…

Caveat: Printing to System.out across threads does not always appear in chronological order. Always include time stamps, and study carefully to verify order of events.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • Thanks for your comment. I have changed to use `Executor` instead of `Timer`. I have also added a `while loop` for checking when to terminate the `scheduled executor`. However, I found that when the `while loop` is added, I cannot start other two schedulers. Is it kind of `thread` issue? But I think I have started the scheduler in different thread – WILLIAM Jun 20 '22 at 03:38
  • @WILLIAM I added a rough draft of some code to guide you. Caution: I am not sure if the shutdown is correct. – Basil Bourque Jun 20 '22 at 07:37
1

Use Quartz Enterprise Job Scheduler

Trigger trigger = TriggerBuilder.newTrigger()
  .withIdentity("myTrigger", "group1")
  .startNow()
  .withSchedule(SimpleScheduleBuilder.simpleSchedule()
    .withIntervalInSeconds(40)
    .repeatForever())
  .build();

Introduction to Quartz

Eskandar Abedini
  • 2,090
  • 2
  • 13