2016-10-27 39 views
0

我有一个ExecutorService坐在一个singleton类中,它接收来自许多不同类的任务。在应用程序关闭时,我需要等待池被清空,然后才允许应用程序退出。阻止Java应用程序退出,直到ThreadPool为空

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; 
} 

在使用本NotificationService,它经常发生,我重新启动应用程序和executorService还没有处理完所有的通知。

对于测试,我可以手动关闭executorService并等待所有任务完成。

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

覆盖finalize方法并等待直到池为空是否可靠和安全?从我读过的内容来看,并不总是调用finalize,尤其是在使用单例类时。

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

此代码包含在将包含在另一个应用程序库,所以没有主要的方法,我可以等到池是空的,除非我用它这样做是不强迫人大。

什么是正确的方式来拖延应用程序(一段合理的时间)从终止,直到池是空的?

+0

你的实现有什么问题? canExit()会阻止,直到执行器完成或30秒。把它放在一个循环中,直到它返回true,你应该没问题。 – Fildor

+0

其他问题:实际处理所有通知至关重要吗?或者你能否比你能够更快地“取消”它们? - 当然,给定一个关机场景。 – Fildor

+0

@Fidor,当我控制一切时,canExit可以工作,如前所述,这将用于库中,因此技术上我可以从shutdownhook调用canExit。在关机时,我至少有机会备份尚未发送的通知,所以即使很可能只有一个或两个未处理的项目在池中,我也不会丢失它们过程,那就是我以前的做法,当有人关闭Tomcat时处理最后一个或两个通知。 –

回答

2

您可以使用addShutdownHook来捕获进程终止事件并在那里等待池。

例如:

Runtime.getRuntime().addShutdownHook(new Thread() { 
     public void run() { 
     NotificationService service = getInstance(); 
     service.executorService.shutdown(); 
     service.executorService.awaitTermination(30, TimeUnit.SECONDS); 
     } 
    }); 
+0

'运行时加addShutdownHook'正是我所期待的,谢谢@Shloim! –

1

这里回答:Java Finalize method call when close the application 终结默认情况下不和做这个功能在退出运行已被弃用。

一个共同的建议是使用Runtime.addShutdownHook但要注意以下在线文档的:

关闭挂钩还应该迅速完成其工作。当一个程序调用退出时,期望虚拟机会立即关闭并退出。当由于用户注销或系统关闭而导致虚拟机终止时,底层操作系统可能只允许关闭并退出的固定时间。因此,尝试任何用户交互或在关闭钩子中执行长时间运行计算是不可取的。

诚实地说,确保所有事情得到适当清理的最好方法是拥有自己的应用程序生命周期,您甚至可以在要求VM退出之前结束自己的应用程序生命周期。

+0

不幸的是,我对应用程序生命周期没有任何控制权,它是一个遗留系统,我只是为它建立一个库。 shutdownhook可以完成任务,当时我无法处理的所有内容,我只会转储到队列或数据库中,但至少在关闭挂钩时,我有机会在丢失任何数据之前清理队列。 –

1

不要在库中使用阻塞关闭钩子或任何类似的东西。你永远不知道图书馆是如何被使用的。所以它应该总是取决于使用你的库的代码在关闭时采取明智的行动。

当然,你必须提供必要的API,例如,加入生命周期的方法到类:

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) { 
     ... 
    } 
} 

然后,应用程序代码可以采取必要的行动来处理你的服务,例如:

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 
     } 
    } 
} 

顺便说一句:避免使用单身人士,如果可行的话。

+0

幸运的是,在这种情况下,我确切知道库将如何使用,并且将使用它的代码(遗留代码挂钩到现在将调用我的库的传统方法中)不会调用手动关闭,因此关闭在这种情况下,钩子是我能做的最好的。单身人士怎么了?在这种情况下,由于执行通知所需的资源(遗留代码)非常大,因此只有一个实例非常好,因此单例运行效果很好。这是Threadsafe,所以一个实例IMO应该没问题。 –

+0

@JanVladimirMostert如果遗留代码不支持生命周期处理的任何方式,那么shutdownhook当然是您最后的(也是唯一的)解决方案。尽管如此,你可以尝试在没有钩子的情况下实现你的服务,并且有一些安装钩子的“丑陋的包装器”或者“丑陋的初始化代码”。这使得测试更容易,例如你可以编写单元测试而不用担心钩子。 – isnot2bad

+0

@JanVladimirMostert谷歌“单身是邪恶的”。可能不适合你的情况。一个原因是可测试性(再次):使用单例服务的代码很难测试,因为您不能简单地使用服务存根。但我不得不承认:有时使用单例比任何其他(复杂)解决方案更容易和直接。 – isnot2bad