6
\$\begingroup\$
 public static void main(String args[]) {
        solution(new Command() {
            @Override
            public void execute() {
                {
                    System.out.println("Hello World");
                }
            }
        }, 1000);

        // java 8
        solution(() -> System.out.println("Hello World"), 1000);
    }

    public static void solution(Command command, int n) {
        new java.util.Timer().schedule(new java.util.TimerTask() {
            @Override
            public void run() {
                command.execute();
            }
        }, n);

    }

    interface Command {
        public void execute();
    }

}

Can my solution be improved? I have used command pattern. Is there a better way to do this?

\$\endgroup\$

1 Answer 1

8
\$\begingroup\$

Imports

Use import java.util.Timer; and import java.util.TimerTask; at the top of the file. This will allow you to simplify this:

new java.util.Timer().schedule(new java.util.TimerTask() {

to this:

new Timer().schedule(new TimerTask() {

which requires less typing and less reading, and so is arguably easier to understand.


Naming

command is a decent name for a Command argument; n is a terrible name for an int. Is that the count of the number of command objects you want submitted? You have to consult the documentation for Timer#schedule() to discover that it is the number of milliseconds to wait before executing the command.

milliseconds, or delay_ms would be better names.

Similarly, solution() is a terrible name. Solution to what? schedule_command() might be a better choice.


Reuse existing interfaces

The difference between interface Command and interface Runnable is that the former uses execute() where as the latter uses run(). Oh, and the latter also adds the marker @FunctionalInterface.

By creating your own Command interface, you would force anyone who wants to use your scheduler, who already has classes that implement the standard Runnable interface, to add another interface to their class and forward the execute() method to the run() method (or vis versa).


Timers and Threads

Every Command passed to solution() will cause the creation of a new Timer() object, with its own timer thread. Each Timer object can schedule multiple tasks. Additionally, all tasks owned by a Timer execute sequentially in the same thread, reducing concurrency issues. Consider:

for (int i=0; i<3; i++) {
    solution( () -> { System.out.print("Hello from command #");
                      Thread.sleep(1);
                      System.out.print(i);
                      Thread.sleep(1);
                      System.out.println("!"); }, 1000 );
}

When executed, the output might become:

Hello from command #Hello from command #0Hello from command #!
12!
!

If your scheduler used a single, common Timer object ...

private static Timer timer = new Timer();

public static void solution(Command command, int n) {
    timer.schedule(new TimerTask() {
        @Override
        public void run() {
            command.execute();
        }
    }, n);
}

Then you would be guaranteed this output instead:

Hello from command #0!
Hello from command #1!
Hello from command #2!

even though each Command was scheduled to run at the same time.


Program Termination

From the Timer documentation:

After the last live reference to a Timer object goes away and all outstanding tasks have completed execution, the timer's task execution thread terminates gracefully (and becomes subject to garbage collection). However, this can take arbitrarily long to occur.

If you want your program to terminate quickly, you need to call .cancel() on all Timer objects you create, once the last scheduled task has completed (or at least been started). This could be done by scheduling a task to cancel the task timer:

timer.schedule( () -> timer.cancel() );

Job Control

You can .cancel() a TimerTask before it has begun executing. However, nothing is returned from schedule(), so the caller cannot cancel any submitted tasks.


Better Implementation

Again, from the Timer documentation:

Java 5.0 introduced the java.util.concurrent package and one of the concurrency utilities therein is the ScheduledThreadPoolExecutor ...

private static ScheduledExecutorService executor;

public static void main(String args[]) {
    executor = Executors.newSingleThreadScheduledExecutor();

    ScheduledFuture<?> future = solution( () -> System.out.println("Hello World"), 1000);

    // Use future here, if desired.

    // Cleanup
    executor.shutdown();
    executor = null;
}

public static ScheduledFuture<?> solution(Runnable command, int delay_ms) {
    return executor.schedule( command, delay_ms, TimeUnit.MILLISECONDS );
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.