Java 线程和执行器的正常关闭
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3332832/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Graceful shutdown of threads and executor
提问by user378101
The following piece of code tries to accompolish this.
下面的一段代码试图实现这一点。
The code loops forever and checks if there are any pending requests to be processed. If there is any, it creates a new thread to process the request and submits it to the executor. Once all the threads are done,it sleeps for 60 seconds and again checks for pending requests.
代码永远循环并检查是否有任何待处理的请求要处理。如果有,它会创建一个新线程来处理请求并将其提交给执行器。一旦所有线程都完成,它会休眠 60 秒并再次检查挂起的请求。
public static void main(String a[]){
//variables init code omitted
ExecutorService service = Executors.newFixedThreadPool(15);
ExecutorCompletionService<Long> comp = new ExecutorCompletionService<Long>(service);
while(true){
List<AppRequest> pending = service.findPendingRequests();
int noPending = pending.size();
if (noPending > 0) {
for (AppRequest req : pending) {
Callable<Long> worker = new RequestThread(something, req);
comp.submit(worker);
}
}
for (int i = 0; i < noPending; i++) {
try {
Future<Long> f = comp.take();
long name;
try {
name = f.get();
LOGGER.debug(name + " got completed");
} catch (ExecutionException e) {
LOGGER.error(e.toString());
}
} catch (InterruptedException e) {
LOGGER.error(e.toString());
}
}
TimeUnit.SECONDS.sleep(60);
}
}
My question is most of the processing done by these threads deal with database. And this program will run on a windows machine. What happens to these threads when someone tries to shutdown or logoff the machine.? How to gracefully shutdown the running threads and also the executor.?
我的问题是这些线程完成的大部分处理都与数据库有关。这个程序将在 Windows 机器上运行。当有人试图关闭或注销机器时,这些线程会发生什么?如何优雅地关闭正在运行的线程和执行器。?
采纳答案by Tim Bender
A typical orderly shutdown of an ExecutorService might look something like this:
ExecutorService 的典型有序关闭可能如下所示:
final ExecutorService executor;
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
executor.shutdown();
if (!executor.awaitTermination(SHUTDOWN_TIME)) { //optional *
Logger.log("Executor did not terminate in the specified time."); //optional *
List<Runnable> droppedTasks = executor.shutdownNow(); //optional **
Logger.log("Executor was abruptly shut down. " + droppedTasks.size() + " tasks will not be executed."); //optional **
}
}
});
*You can log that the executor still had tasks to process after waiting the time you were willing to wait.
**You can attempt to force the executor's worker Threads to abandon their current tasks and ensure they don't start any of the remaining ones.
*您可以记录执行器在等待您愿意等待的时间后仍有任务要处理。
**您可以尝试强制执行器的工作线程放弃其当前任务并确保它们不会启动任何剩余的任务。
Note that the solution above will work when a user issues an interrupt to your java
process or when your ExecutorService
only contains daemon threads. If, instead, the ExecutorService
contains non-daemon threads that haven't completed, the JVM won't try to shutdown, and therefore the shutdown hooks won't be invoked.
请注意,当用户向您的java
进程发出中断或您ExecutorService
仅包含守护线程时,上述解决方案将起作用。相反,如果ExecutorService
包含尚未完成的非守护进程线程,则 JVM 不会尝试关闭,因此不会调用关闭挂钩。
If attempting to shutdown a process as part of a discrete application lifecycle (not a service) then shutdown code should not be placed inside a shutdown hook but at the appropriate location where the program is designed to terminate.
如果尝试将进程作为离散应用程序生命周期的一部分(而不是服务)关闭,那么关闭代码不应放置在关闭挂钩内,而应放置在程序旨在终止的适当位置。
回答by Enno Shioji
The book "Java Concurrency in Practice" states:
《Java 并发实践》一书指出:
7.4. JVM Shutdown
The JVM can shut down in either an orderly or abrupt manner. An orderly shutdown is initiated when the last "normal" (nondaemon) thread terminates, someone calls System.exit, or by other platform-specific means (such as sending a SIGINT or hitting Ctrl-C). [...]
7.4.1. Shutdown Hooks
In an orderly shutdown, the JVM first starts all registered shutdown hooks. Shutdown hooks are unstarted threads that are registered with Runtime.addShutdownHook. The JVM makes no guarantees on the order in which shutdown hooks are started. If any application threads (daemon or nondaemon) are still running at shutdown time, they continue to run concurrently with the shutdown process. When all shutdown hooks have completed, the JVM may choose to run finalizers if runFinalizersOnExit is true, and then halts. The JVM makes no attempt to stop or interrupt any application threads that are still running at shutdown time; they are abruptly terminated when the JVM eventually halts. If the shutdown hooks or finalizers don't complete, then the orderly shutdown process "hangs" and the JVM must be shut down abruptly. [...]
7.4. JVM 关闭
JVM 可以以有序或突然的方式关闭。当最后一个“正常”(非守护进程)线程终止、有人调用 System.exit 或通过其他特定于平台的方式(例如发送 SIGINT 或按 Ctrl-C)时,将启动有序关闭。[...]
7.4.1. 关机钩子
在有序关闭中,JVM 首先启动所有已注册的关闭挂钩。关闭钩子是使用 Runtime.addShutdownHook 注册的未启动线程。JVM 不保证关闭挂钩的启动顺序。如果任何应用程序线程(守护进程或非守护进程)在关闭时仍在运行,它们将继续与关闭进程同时运行。当所有关闭钩子都完成后,如果 runFinalizersOnExit 为真,JVM 可能会选择运行终结器,然后停止。JVM 不会尝试停止或中断在关闭时仍在运行的任何应用程序线程;当 JVM 最终停止时,它们会突然终止。如果关闭挂钩或终结器未完成,则有序关闭过程“挂起” 并且必须突然关闭 JVM。[...]
The important bits are, "The JVM makes no attempt to stop or interrupt any application threads that are still running at shutdown time; they are abruptly terminated when the JVM eventually halts."so I suppose the connection to the DB will abruptly terminate, if no shutdown hooks are there to do a graceful clean up (if you are using frameworks, they usually do provide such shutdown hooks). In my experience, session to the DB can remain until it is timed out by the DB, etc. when the app. is terminated without such hooks.
重要的一点是, “JVM 不会尝试停止或中断任何在关闭时仍在运行的应用程序线程;当 JVM 最终停止时,它们会突然终止。” 所以我想与数据库的连接会突然终止,如果没有关闭挂钩进行正常清理(如果您使用框架,它们通常会提供这样的关闭挂钩)。根据我的经验,到 DB 的会话可以一直保持到 DB 等应用程序超时时为止。没有这样的钩子就终止了。
回答by skaffman
You can either call shutdown()
on the ExecutorService
:
你可以调用shutdown()
的ExecutorService
:
Initiates an orderly shutdown in which previously submitted tasks are executed, but no new tasks will be accepted.
启动有序关闭,其中执行先前提交的任务,但不会接受新任务。
or you can call shutdownNow()
:
或者你可以打电话shutdownNow()
:
Attempts to stop all actively executing tasks, halts the processing of waiting tasks, and returns a list of the tasks that were awaiting execution.
There are no guarantees beyond best-effort attempts to stop processing actively executing tasks. For example, typical implementations will cancel via Thread.interrupt(), so any task that fails to respond to interrupts may never terminate.
尝试停止所有正在执行的任务,停止等待任务的处理,并返回等待执行的任务列表。
除了尽最大努力停止处理正在执行的任务之外,没有任何保证。例如,典型的实现将通过 Thread.interrupt() 取消,因此任何未能响应中断的任务可能永远不会终止。
Which one you call depends how badly you want it to stop....
您调用哪一个取决于您希望它停止的严重程度....
回答by Georgi Peev
Since adding a shutdown hook to explicitly call shutdown()
didn't work for me, I found an easy solution in Google's Guava:
com.google.common.util.concurrent.MoreExecutors.getExitingExecutorService.
由于添加关闭挂钩来显式调用shutdown()
对我不起作用,我在 Google 的 Guava 中找到了一个简单的解决方案:
com.google.common.util.concurrent.MoreExecutors.getExitingExecutorService。
回答by Chinmay Samant
I had similar issue, i use to get error like
我有类似的问题,我曾经得到类似的错误
o.a.c.loader.WebappClassLoaderBase :: The web application [ROOT] appears to have started a thread named [pool-2-thread-1] but has failed to stop it. This is very likely to create a memory leak. Stack trace of thread: sun.misc.Unsafe.park(Native Method) java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
oacloader.WebappClassLoaderBase :: Web 应用程序 [ROOT] 似乎已启动名为 [pool-2-thread-1] 的线程,但未能将其停止。这很可能造成内存泄漏。线程堆栈跟踪: sun.misc.Unsafe.park(Native Method) java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.爪哇:2039)
Bellow code fixed it
波纹管代码修复了它
private ThreadPoolExecutor executorPool;
@PostConstruct
public void init() {
log.debug("Initializing ThreadPoolExecutor");
executorPool = new ThreadPoolExecutor(1, 3, 1, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(1));
}
@PreDestroy
public void destroy() {
log.debug("Shuting down ThreadPoolExecutor");
executorPool.shutdown();
}