2

I have a thread which executes code periodically, e. g. every 10 seconds. I'd like to have the option to also invoke the same code in a spontaneous way and not have to wait 10 seconds. But the code of the automatic and spontaneous execution must never run concurrently, instead they should run in sequence if the user presses the execute button while the thread is invoking the same method.

Does anyone know a good pattern or even a class that can address this kind of requirement?

First thing that comes to mind would be to make the work method synchronized. But in that case the manual execution (e. g. button press) is blocked and has to wait until the method in the thread is finished. Is there a better approach without blocking?

Example:

public class Executor extends Thread {

    // endless loop, executes work method periodically with pause inbetween
    @Override
    public void run() {

        while( true) {

            work( "automatic");

            pause(10000);

        }

    }

    // Working method that's executed periodically or manually
    private synchronized void work( String text) {

        System.out.println( "Working " + text + " " + System.currentTimeMillis());

    }

    // helper method that pauses the thread
    private static void pause( long sleepMs) {
        try {

            Thread.sleep(sleepMs);

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {

        // start automatic execution
        Executor executor = new Executor();
        executor.start();

        // pause a while
        pause(1000);

        // manual execution
        executor.work( "manual");

    }

}

Edit: Solution for my requirement:

public class ScheduledExecutor {

    public static void main(String[] args) throws InterruptedException {

        ScheduledThreadPoolExecutor executor = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(1);
        executor.scheduleWithFixedDelay(new Work("auto"), 0, 10, TimeUnit.SECONDS);

        Thread.sleep(1000);

        executor.execute(new Work("manual"));

    }

    public static class Work implements Runnable {

        String text;

        public Work(String text) {
            this.text = text;
        }

        @Override
        public void run() {

            System.out.println("Working " + text + " " + System.currentTimeMillis());

        }
    }

}
Roland
  • 18,114
  • 12
  • 62
  • 93
  • Interrupt the sleep? – Thorbjørn Ravn Andersen May 29 '17 at 16:10
  • What happens if the thread gets interrupted while it isn't in sleep mode? – Roland May 29 '17 at 16:28
  • Can you update your question to make it clear how exactly you want it to work. You're saying you don't want the thread code of manual and automatic to run concurrently. Then you say you don't want the manual execution to be blocked when you press a button... – Allan May 29 '17 at 16:55
  • To emphasize Allan's point: if the task is running on the timer, and a button is pushed, do you 1) abort the running, timer-initiated execution, and immediately execute the button-initiated task, or 2) skip the button-initiated task altogether, or 3) block, waiting for the timer-initiated execution to complete, then run the button-initiated task? – erickson May 29 '17 at 17:33
  • Done. It's option 3, i. e. wait for the thread to complete and after that run the manually initiated task. – Roland May 29 '17 at 18:50

3 Answers3

4

I would create a new, single-thread executor service:

ExecutorService executorService = Executors.newFixedThreadPool(1);

Then, I would set up a timer that feeds the executorService a task once every 10 seconds.

new Timer(10000, new ActionListener {
    public void actionPerformed(ActionEvent evt) {
        executorService.execute(() -> doWhatever());
    }
}).start();

Finally, you can call executorService.execute(() -> doWhatever()); in your button press handler, or wherever else you want in your code.

Only one activation of doWhatever() will run at a time because the executorService has only one thread on which to run them. And, your button press handler will never have to wait, because it does nothing but put a new object on a queue.

Solomon Slow
  • 25,130
  • 5
  • 37
  • 57
  • 1
    Thanks! That got me into the right direction. Instead of a Timer using a `ScheduledThreadPoolExecutor` and invoking the working method with `scheduleWithFixedDelay` matches my requirement better. – Roland May 30 '17 at 04:52
3

I have a thread which executes code periodically, e. g. every 10 seconds. I'd like to have the option to also invoke the same code in a spontaneous way and not have to wait 10 seconds.

A simple way to do this in your code is not to pause by using Thread.sleep(...) but rather do wait(...). Then whenever you want the command to wakeup and run manually it just does a notify().

So you code would look something like:

while( true) {
    work( "automatic");
    synchronized (this) {
       try {
           // wait for a bit but allow someone else to awake us to run manually
           wait(10000);
       } catch (InterruptedException ie) {
           // always a good pattern
           Thread.currentThread().interrupt();
           return;
       }
    }
}

Then when you want to have it run manually you do:

synchronized (executor) {
    executor.notify();
}

The notify will awaken the thread immediately so that it can run it's task. The work method then does not need to be synchronized because only the Executor thread is running it.

NOTE: As pointed out by @shinobi, using wait() like this could suffer from spurious wake-ups which can happen with certain OS thread implementations.

Lastly, it is a better practice to make Executor implement Runnable as opposed to extending Thread.

Gray
  • 115,027
  • 24
  • 293
  • 354
  • Beautifully minimal and simple, but not protected against spurious wake-ups (https://stackoverflow.com/questions/13148001/spurious-wakeups-wait-and-notifyall). – shinobi May 29 '17 at 18:13
  • Good point. Guess it depends on whether or not extra runs is a problem @shinobi. – Gray May 29 '17 at 21:29
0

Share a semaphore between the server thread (the one that executes the task) and client threads (the ones that need to trigger immediate execution):

    Semaphore sem = new Semaphore( 0 );

The server thread needs to execute the following code (note that it's an endless loop — you'll likely want to plug-in your program termination check as the condition to while()):

    while( true ) {
        try {
            sem.tryAcquire( 10, TimeUnit.SECONDS );
        } catch( InterruptedException e ) {
            continue;
        }

        runTask();
        sem.drainPermits();
    }

Then, in order to trigger immediate execution, the client thread needs to do:

    sem.release();

Thus, the server thread will execute the task upon either acquiring a permit from the semaphore as soon as a client thread releases one (triggered immediate execution,) or timing-out in Semaphore.tryAcquire() (periodic executions 10s apart, end-to-start.) Having executions 10s apart start-to-start will take some slightly more involved logic, as well as keeping track of last execution's starting time, but the basic idea remains the same.

You need to drain the permits each time in order to avoid multiple back-to-back executions of the task, in cases where it might be triggered for immediate execution while still being executed.

shinobi
  • 351
  • 1
  • 8