ShutdownUtil { public static boolean boundedShutdown(List<Runnable> shutdownTasks, String message, long timeoutMs) { ExecutorService shutdownExecutorService = Executors.newCachedThreadPool( new ThreadFactoryBuilder().setNameFormat(message + "-%d").setDaemon(true).build()); shutdownTasks.forEach(shutdownExecutorService::submit); shutdownExecutorService.shutdown(); try { shutdownExecutorService.awaitTermination(timeoutMs, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { LOG.error("Shutdown was interrupted for " + message, e); } if (shutdownExecutorService.isTerminated()) { LOG.info("Shutdown complete for {}", message); return true; } else { LOG.error("Shutdown function for {} remains unfinished after timeout ({} ms) or interruption", message, timeoutMs); ThreadUtil.logThreadDump(message); shutdownExecutorService.shutdownNow(); return false; } }  static boolean boundedShutdown(List<Runnable> shutdownTasks, String message, long timeoutMs);  }
@Test public void testBoundedShutdown() throws Exception { long longTimeout = Duration.ofSeconds(60).toMillis(); long shortTimeout = Duration.ofMillis(100).toMillis(); Runnable shortRunnable = () -> { try { Thread.sleep(shortTimeout); } catch (Exception e) { Assert.fail(e.getMessage()); } }; long start = System.currentTimeMillis(); Assert.assertTrue("expect the shutdown task to terminate", ShutdownUtil.boundedShutdown(Collections.singletonList(shortRunnable), "testLongTimeout", longTimeout)); long end = System.currentTimeMillis(); Assert.assertTrue("boundedShutdown should complete if the shutdown function completes earlier", (end - start) < longTimeout / 2); Runnable longRunnable = () -> { try { Thread.sleep(longTimeout); } catch (Exception e) { Assert.fail(e.getMessage()); } }; start = System.currentTimeMillis(); Assert.assertFalse("expect the shutdown task to be unfinished", ShutdownUtil.boundedShutdown(Collections.singletonList(longRunnable), "testShortTimeout", shortTimeout)); end = System.currentTimeMillis(); Assert.assertTrue("boundedShutdown should complete even if the shutdown function takes long time", (end - start) < longTimeout / 2); }