0

I've got an ExecutorService sitting inside a singleton class which receives tasks from many different classes. On application shutdown, I need to wait for the pool to be empty before I allow the application to exit.

private static NotificationService instance = null;

private ExecutorService executorService = Executors.newFixedThreadPool(25);

public static synchronized NotificationService getInstance() {
    if (instance == null) {
        instance = new NotificationService(true);
    }
    return instance;
}

While using this NotificationService, it frequently happens that I restart the application and the executorService hasn't finished processing all the notifications.

For Testing, I can manually shutdown the executorService and wait until all tasks are completed.

public static boolean canExit() throws InterruptedException {
    NotificationService service = getInstance();
    service.executorService.shutdown();
    service.executorService.awaitTermination(30, TimeUnit.SECONDS);
    return service.executorService.isTerminated();
}

Is it reliable and safe to override the finalize method and wait there until the pool is empty? From what I've read, finalize is not always called, especially not when using a singleton class.

@Override
protected void finalize() throws Throwable {
    while (!canExit()){
        Thread.sleep(100);
    }
    super.finalize();
}

This code is included in a library that will be included in another application, so there's no main method where I can wait until the pool is empty, unless I force the person using it to do so which is not great.

What is the correct way to stall the application (for a reasonable amount of time) from terminating until the pool is empty?

Jan Vladimir Mostert
  • 12,380
  • 15
  • 80
  • 137
  • What's wrong with your implementation? canExit() will block until the executor is done or 30 seconds. Put it in a loop until it returns true and you should be fine. – Fildor Oct 27 '16 at 11:20
  • Other question: Is it crucial to actually process all notifications? Or could you as well "cancel" them quicker than you can process them? - Given a shutdown scenario of course. – Fildor Oct 27 '16 at 11:22
  • @Fidor, canExit works when I control everything, as stated before, this will be used in a library, so technically I could be calling canExit from a shutdownhook. On shutdown, I at least get a chance to backup the notifications that hasn't been sent yet so I don't lose them even though it's more than likely that there will only be one or two unprocessed items in the pool which I can just process, that's what I was after, handling those last one or two notifications when somebody shuts down Tomcat. – Jan Vladimir Mostert Oct 27 '16 at 11:59

3 Answers3

2

You can use addShutdownHook to catch the process termination event and wait for the pool there.

example:

 Runtime.getRuntime().addShutdownHook(new Thread() {
      public void run() {
        NotificationService service = getInstance();
        service.executorService.shutdown();
        service.executorService.awaitTermination(30, TimeUnit.SECONDS);
      }
    });
Shloim
  • 5,281
  • 21
  • 36
1

Answered here: Java Finalize method call when close the application Finalizers do not run on exit by default and the functionality to do this is deprecated.

One common advice is to use the Runtime.addShutdownHook but be aware of the following line of documentation:

Shutdown hooks should also finish their work quickly. When a program invokes exit the expectation is that the virtual machine will promptly shut down and exit. When the virtual machine is terminated due to user logoff or system shutdown the underlying operating system may only allow a fixed amount of time in which to shut down and exit. It is therefore inadvisable to attempt any user interaction or to perform a long-running computation in a shutdown hook.

In all honesty the best way to ensure everything gets properly cleaned up is to have your own application lifecycle which you can end before you even ask the VM to exit.

Community
  • 1
  • 1
Kiskae
  • 24,655
  • 2
  • 77
  • 74
  • Unfortunately I have zero control over the application lifecycle, it's a legacy system and I'm just building a library for it. A shutdownhook gets the job done, everything that I can't process at the time, I will simply dump on a queue or in DB, but at least with a shutdown hook, I get a chance to cleanup the queue before I lose any data. – Jan Vladimir Mostert Oct 27 '16 at 11:53
1

Don't use blocking shutdown hooks or anything similar in a library. You never know how the library is meant to be used. So it should always be up to the code that is using your library to take sensible actions on shut down.

Of course, you have to provide the necessary API for that, e.g. by adding lifecycle-methods to your class:

public class NotificationService {
    ...

    public void start() {
        ...
    }

    /**
     * Stops this notification service and waits until
     * all notifications have been processed, or a timeout occurs.
     * @return the list of unprocessed notification (in case of a timeout),
               or an empty list.
     */
    public List<Notification> stop(long timeout, TimeUnit unit) {
        service.shutdown();
        if (!service.awaitTermination(timeout, unit)) {
            List<Runnable> tasks = service.shutdownNow();
            return extractNotification(tasks);
        }
        return Collections.emptyList();
    }

    private List<Notification> extractNotification(List<Runnable> tasks) {
        ...
    }
}

Then, the application code can take the required actions to handle your service, e.g.:

public static void main(String[] args) {
    NotificationService service = new NotificationService(...);
    service.start();
    try {
        // use service here
    } finally {
        List<Notification> pending = service.stop(30, TimeUnit.SECONDS);
        if (!pending.isEmpty()) {
            // timeout occured => handle pending notifications
        }
    }
}

Btw.: Avoid using singletons, if feasible.

isnot2bad
  • 24,105
  • 2
  • 29
  • 50
  • Fortunately in this case, I know exactly how the library will be used and the code that will be using it (legacy code hooking into a legacy method that will now be calling my library) will not be calling a manual shutdown, so a shutdown hook is the best I can do in this scenario. What's wrong with a singleton? In this scenario, the singleton works great since the resources required to do notifications (legacy code) is quite big, so only having one instance of that is great. It's Threadsafe, so a single instance IMO should be fine. – Jan Vladimir Mostert Oct 27 '16 at 11:56
  • @JanVladimirMostert If the legacy code does not support any way to do lifecycle-handling, of course a shutdownhook will be your last (and only?) solution. Nevertheless, you could try to implement your service without hook and have some kind of "ugly wrapper" or "ugly initialization code" that installs the hook. This makes testing much easier, e.g. you can write unit tests without worrying about the hook. – isnot2bad Oct 27 '16 at 12:21
  • @JanVladimirMostert Google "singletons are evil". Might not be appropriate in your case. One reason is testability (again): Code that uses a singleton-service is hard to test as you can't simply use a service-stub instead. But I have to admit: Sometimes using a singleton is much easier and straightforward than any other (complex) solution. – isnot2bad Oct 27 '16 at 12:24
  • Makes perfect sense!. Code has zero unit tests (it's a section of the code-base that deals with integrated external services which is rather difficult to test in a unit test in any case), so a singleton is fine in this scenario. – Jan Vladimir Mostert Oct 27 '16 at 12:29
  • Then go with it! ;) – isnot2bad Oct 27 '16 at 12:31