Coordinated stop before destroy when ExecutorService not terminated yet
Closes gh-31549
This commit is contained in:
parent
38724a1205
commit
9414c2ddba
|
|
@ -86,6 +86,8 @@ public abstract class ExecutorConfigurationSupport extends CustomizableThreadFac
|
||||||
@Nullable
|
@Nullable
|
||||||
private ExecutorLifecycleDelegate lifecycleDelegate;
|
private ExecutorLifecycleDelegate lifecycleDelegate;
|
||||||
|
|
||||||
|
private volatile boolean lateShutdown;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the ThreadFactory to use for the ExecutorService's thread pool.
|
* Set the ThreadFactory to use for the ExecutorService's thread pool.
|
||||||
|
|
@ -127,8 +129,9 @@ public abstract class ExecutorConfigurationSupport extends CustomizableThreadFac
|
||||||
* <p>Default is {@code false} as of 6.1, triggering an early soft shutdown of
|
* <p>Default is {@code false} as of 6.1, triggering an early soft shutdown of
|
||||||
* the executor and therefore rejecting any further task submissions. Switch this
|
* the executor and therefore rejecting any further task submissions. Switch this
|
||||||
* to {@code true} in order to let other components submit tasks even during their
|
* to {@code true} in order to let other components submit tasks even during their
|
||||||
* own destruction callbacks, at the expense of a longer shutdown phase.
|
* own stop and destruction callbacks, at the expense of a longer shutdown phase.
|
||||||
* This will usually go along with
|
* The executor will not go through a coordinated lifecycle stop phase then
|
||||||
|
* but rather only stop tasks on its own shutdown. This usually goes along with
|
||||||
* {@link #setWaitForTasksToCompleteOnShutdown "waitForTasksToCompleteOnShutdown"}.
|
* {@link #setWaitForTasksToCompleteOnShutdown "waitForTasksToCompleteOnShutdown"}.
|
||||||
* <p>This flag will only have effect when the executor is running in a Spring
|
* <p>This flag will only have effect when the executor is running in a Spring
|
||||||
* application context and able to receive the {@link ContextClosedEvent}.
|
* application context and able to receive the {@link ContextClosedEvent}.
|
||||||
|
|
@ -144,9 +147,13 @@ public abstract class ExecutorConfigurationSupport extends CustomizableThreadFac
|
||||||
/**
|
/**
|
||||||
* Set whether to wait for scheduled tasks to complete on shutdown,
|
* Set whether to wait for scheduled tasks to complete on shutdown,
|
||||||
* not interrupting running tasks and executing all tasks in the queue.
|
* not interrupting running tasks and executing all tasks in the queue.
|
||||||
* <p>Default is {@code false}, shutting down immediately through interrupting
|
* <p>Default is {@code false}, with a coordinated lifecycle stop first (unless
|
||||||
* ongoing tasks and clearing the queue. Switch this flag to {@code true} if
|
* {@link #setAcceptTasksAfterContextClose "acceptTasksAfterContextClose"}
|
||||||
* you prefer fully completed tasks at the expense of a longer shutdown phase.
|
* has been set) and then an immediate shutdown through interrupting ongoing
|
||||||
|
* tasks and clearing the queue. Switch this flag to {@code true} if you
|
||||||
|
* prefer fully completed tasks at the expense of a longer shutdown phase.
|
||||||
|
* The executor will not go through a coordinated lifecycle stop phase then
|
||||||
|
* but rather only stop and wait for task completion on its own shutdown.
|
||||||
* <p>Note that Spring's container shutdown continues while ongoing tasks
|
* <p>Note that Spring's container shutdown continues while ongoing tasks
|
||||||
* are being completed. If you want this executor to block and wait for the
|
* are being completed. If you want this executor to block and wait for the
|
||||||
* termination of tasks before the rest of the container continues to shut
|
* termination of tasks before the rest of the container continues to shut
|
||||||
|
|
@ -374,7 +381,7 @@ public abstract class ExecutorConfigurationSupport extends CustomizableThreadFac
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void stop() {
|
public void stop() {
|
||||||
if (this.lifecycleDelegate != null) {
|
if (this.lifecycleDelegate != null && !this.lateShutdown) {
|
||||||
this.lifecycleDelegate.stop();
|
this.lifecycleDelegate.stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -386,9 +393,12 @@ public abstract class ExecutorConfigurationSupport extends CustomizableThreadFac
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void stop(Runnable callback) {
|
public void stop(Runnable callback) {
|
||||||
if (this.lifecycleDelegate != null) {
|
if (this.lifecycleDelegate != null && !this.lateShutdown) {
|
||||||
this.lifecycleDelegate.stop(callback);
|
this.lifecycleDelegate.stop(callback);
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
callback.run();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -439,10 +449,16 @@ public abstract class ExecutorConfigurationSupport extends CustomizableThreadFac
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void onApplicationEvent(ContextClosedEvent event) {
|
public void onApplicationEvent(ContextClosedEvent event) {
|
||||||
if (event.getApplicationContext() == this.applicationContext && !this.acceptTasksAfterContextClose) {
|
if (event.getApplicationContext() == this.applicationContext) {
|
||||||
// Early shutdown signal: accept no further tasks, let existing tasks complete
|
if (this.acceptTasksAfterContextClose || this.waitForTasksToCompleteOnShutdown) {
|
||||||
// before hitting the actual destruction step in the shutdown() method above.
|
// Late shutdown without early stop lifecycle.
|
||||||
initiateShutdown();
|
this.lateShutdown = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Early shutdown signal: accept no further tasks, let existing tasks complete
|
||||||
|
// before hitting the actual destruction step in the shutdown() method above.
|
||||||
|
initiateShutdown();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,7 @@ final class ExecutorLifecycleDelegate implements SmartLifecycle {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isRunning() {
|
public boolean isRunning() {
|
||||||
return (!this.executor.isShutdown() & !this.paused);
|
return (!this.paused && !this.executor.isTerminated());
|
||||||
}
|
}
|
||||||
|
|
||||||
void beforeExecute(Thread thread) {
|
void beforeExecute(Thread thread) {
|
||||||
|
|
|
||||||
|
|
@ -344,9 +344,10 @@ public class EnableSchedulingTests {
|
||||||
@Override
|
@Override
|
||||||
public TaskScheduler myTaskScheduler() {
|
public TaskScheduler myTaskScheduler() {
|
||||||
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
|
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
|
||||||
scheduler.setThreadNamePrefix("explicitScheduler-");
|
|
||||||
scheduler.setAwaitTerminationMillis(1000);
|
|
||||||
scheduler.setPoolSize(2);
|
scheduler.setPoolSize(2);
|
||||||
|
scheduler.setThreadNamePrefix("explicitScheduler-");
|
||||||
|
scheduler.setAcceptTasksAfterContextClose(true);
|
||||||
|
scheduler.setAwaitTerminationMillis(1000);
|
||||||
return scheduler;
|
return scheduler;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue