0

We have a glue component between legacy code and current code. Essentially the whole legacy application is single threaded and has horrible issues where a ui refresh for a single instruction can happen 5 to 8 times.

I want to publish an async message after the first update request happens +2 seconds.

Let's not get stuck on the why, this is not what I want to really do, but I have to understand how to at least do this so I can implement a real solution.

Runnable task = () -> {
    try {
        TimeUnit.SECONDS.sleep(2);
        messageBus.publishAsynch(new LegacyUiUpdateEvent());
    } catch (InterruptedException e) {
        // TODO Log something
        Thread.currentThread().interrupt();
    }
};

@Override
public void update(Observable arg0, Object arg1) {
    ExecutorService executor = Executors.newSingleThreadExecutor();
    if (futureTask == null || futureTask.isDone()) {
        futureTask = executor.submit(task);
        try {
            executor.awaitTermination(10, TimeUnit.SECONDS);
            executor.shutdownNow();
        } catch (InterruptedException e) {
            // TODO Log something
            Thread.currentThread().interrupt();
        }
    }
}

The theory is: If future task doesn't exist, we create it, once it's there, if it's not done (because this is false legacy update 4/x where x ∈ [5,12] and the sleep is still in effect) then we completely skip and don't create a new executor.

The problem is that, from what I can tell, the executor.submit(task) does not in fact happen on a new tread. Like I said the legacy app is single threaded, and after I increased the sleep to 15s it was blindingly obvious that it was sending the whole current thread to sleep.

How would I put my taks on a completely new thread (using the concurrency library) and avoiding doing the task multiple times, even though the update method is being called way way too many times (and that is 100% out of my control). I think the future.isDone() thing works, but not 100%

Kalec
  • 2,681
  • 9
  • 30
  • 49

2 Answers2

2

if you are on Java 8 or higher, this is a better thing to do

CompletableFuture.runAsync(task);

because this will be executed on the Fork-join thread pool, that is managed by the JVM and you will not concern yourself by creating it nor shutting it down. and of course this will run asynchronously which match your requirements .

mehdi maick
  • 325
  • 3
  • 7
1

executor.submit() does start the task in a new thread, but right after executor.awaitTermination(10, TimeUnit.SECONDS); is waiting in the current thread for the task to be completed. There's no need to wait in the current thread, but there does need to be a way to determine if the task is already running.

The messy part is creating the ExecutorService each time - there's no need to recreate it each time. It can be an instance variable of the class and re-used. Ideally, it would be injected through a constructor so the class which created it can shut it down if that's really needed.

 private final ExecutorService executor = Executors.newSingleThreadExecutor();  // or injected through constructor
 private Future<?> futureTask;

@Override
public void update(Observable arg0, Object arg1) {
    if (futureTask == null || futureTask.isDone()) {
        futureTask = executor.submit(task);
    }
}
Andrew S
  • 2,509
  • 1
  • 12
  • 14