7-Cancelletion and Shutdown
7.1-Task cancellation
7.1.1. Interruption
Java does not provide any mechanism
for safely forcing a thread to stop what it is doing.[1] Instead, it provides interruption, a cooperative mechanism that lets
one thread ask another to stop what it is doing.
There is no safe way to preemptively stop a thread in Java, and therefore no safe way to preemptively stop a task.
One such cooperative mechanism is setting a “cancellation requested” flag that the task checks periodically; if it finds
the flag set, the task terminates early.
There is nothing in the API or language specification that ties interruption to any specific cancellation semantics, but in
practice, using interruption for anything but cancellation is fragile and difficult to sustain in larger applications.
Calling interrupt does not necessarily stop the target thread from doing what it is doing; it merely delivers the
message that interruption has been requested.
A good way to think about interruption is that it does not actually interrupt a running thread; it just requests that the
thread interrupt itself at the next convenient opportunity.
Interruption is usually the most sensible way to implement cancellation.
7.1.2. Interruption Policies
An interruption policy
determines how a thread interprets an interruption request.
A task should not assume anything about the interruption policy of its executing thread unless it is explicitly designed to
run within a service that has a specific interruption policy.
Thread.currentThread().interrupt();
Because each thread has its own interruption policy, you should not interrupt a thread unless you know what
interruption means to that thread.
7.1.3. Responding to Interruption
Only code that implements a thread’s interruption policy may swallow an interruption request. General-purpose task
and library code should never swallow interruption requests.
when you call an interruptible blocking method such as Thread.sleep or
BlockingQueue.put, there are two practical strategies for handling InterruptedException:
x Propagate the exception (possibly after some taskͲspecific cleanup), making your method an interruptible
blocking method, too; or
x Restore the interruption status so that code higher up on the call stack can deal with it.
it shares a
deficiency with join: we don’t know if control was returned because the thread exited normally or because the join
timed out.
ExecutorService.submit (mayInterruptIfRunning)Setting this argument to false means “don’t run this task if it hasn’t started yet”, and should be used for
tasks that are not designed to handle interruption.
it is safe to set mayInterruptIfRunning when
cancelling tasks through their Futures when they are running in a standard Executor.
Future.cancel unconditionally in a finally block, taking advantage of the fact that cancelling a
completed task has no effect.
7.1.4. Example: Timed Run
7.1.5. Cancellation Via Future
ExecutorService.submit:mayInterruptIfRunning:Setting this argument to false means “don’t run this task if it hasn’t started yet”, and should be used for
tasks that are not designed to handle interruption.
The task execution threads created by the standard Executor implementations implement an
interruption policy that lets tasks be cancelled using interruption, so it is safe to set mayInterruptIfRunning when
cancelling tasks through their Futures when they are running in a standard Executor.
Future.cancel unconditionally in a finally block, taking advantage of the fact that cancelling a
completed task has no effect.
7.1.6. Dealing with Non-interruptible Blocking
Many blocking library methods respond to interruption by returning early and throwing InterruptedException, if a thread is blocked performing synchronous socket I/O or waiting to
acquire an intrinsic lock, interruption has no effect other than setting the thread’s interrupted status.
ReaderThread overrides interrupt to both deliver
a standard interrupt and close the underlying socket; thus interrupting a ReaderThread makes it stop what it is doing
whether it is blocked in read or in an interruptible blocking method.
7.1.7. Encapsulating Nonstandard Cancellation with Newtaskfor
7.2-Stopping a thread-based service
start
thread ownership is not transitive: the application may own the service and the
service may own the worker threads, but the application doesn’t own the worker threads and therefore should not
attempt to stop them directly. Instead, the service should provide lifecycle methods for shutting itself down that also
shut down the owned threads; then the application can shut down the service, and the service can shut down the
threads.
Provide lifecycle methods whenever a thread-owning service has a lifetime longer than that of the method that created
it.
7.2.1. Example: A Logging Service
The implementation of log is a check-then-act
sequence: producers could observe that the service has not yet been shut down but still queue messages after the
shutdown, again with the risk that the producer might get blocked in log and never become unblocked.
7.2.2. ExecutorService Shutdown
shutdownNow returns the list of tasks that had not yet
started after attempting to cancel all actively executing tasks.
abrupt termination is faster
but riskier because tasks may be interrupted in the middle of execution, and normal termination is slower but safer
because the ExecutorService does not shut down until all queued tasks are processed.
7.2.3. Poison Pills
poison pill: a recognizable object placed
on the queue that means “when you get this, stop.”
Poison pills work only when the number of producers and consumers is known. each producer place a pill on the queue and having the consumer stop only
when it receives N
producers pills.
7.2.4. Example: A One-shot Execution Service
7.2.5. Limitations of Shutdownnow
The Runnable objects returned by shutdownNow might not be the same objects that were submitted to the ExecutorService: they
might be wrapped instances of the submitted tasks.
7.3. Handling Abnormal Thread Termination
7.3.1. Uncaught Exception Handlers
When a thread exits due to an uncaught exception, the JVM reports this event to an application-provided
UncaughtExceptionHandler; if no handler exists, the default behavior is to print the stack trace to
System.err
In Java 5.0 and later, you
can set an UncaughtExceptionHandler on a per-thread basis with Thread.setUncaughtExceptionHandler, and can also set the
default UncaughtExceptionHandler with Thread.setDefaultUncaughtExceptionHandler.
To set an UncaughtExceptionHandler for pool threads, provide a ThreadFactory to the ThreadPoolExecutor
constructor. If you want to be notified when a
task fails due to an exception so that you can take some task-specific recovery action, either wrap the task with a
Runnable or Callable that catches the exception or override the afterExecute hook in ThreadPoolExecutor.
If a task submitted with submit terminates with an exception, it is rethrown by
Future.get, wrapped in an ExecutionException.
7.4-JVM shutdown
7.4.1. Shutdown Hooks
Shutdown hooks can be used for service or application cleanup, such as deleting temporary files or cleaning up
resources that are not automatically cleaned up by the OS.
7.4.2. Daemon Threads
Threads are divided into two types: normal threads and daemon threads. When the JVM starts up, all the threads it
creates (such as garbage collector and other housekeeping threads) are daemon threads, except the main thread.so by default any threads created by
the main thread are also normal threads.
When the JVM halts, any remaining daemon threads are abandoned
Daemon threads are not a good substitute for properly managing the lifecycle of services within an application.
7.4.3. Finalizers
Since finalizers can run in a thread managed by the JVM, any state accessed by a finalizer will be accessed by more than
one thread and therefore must be accessed with synchronization.
In most cases, the combination of finally blocks and explicit close methods does a better
job of resource management than finalizers; work hard to avoid writing or using classes with finalizers.
Summary
Using FutureTask and the Executor framework
simplifies building cancellable tasks and services.
7.1-Task cancellation
7.1.1. Interruption
Java does not provide any mechanism
for safely forcing a thread to stop what it is doing.[1] Instead, it provides interruption, a cooperative mechanism that lets
one thread ask another to stop what it is doing.
There is no safe way to preemptively stop a thread in Java, and therefore no safe way to preemptively stop a task.
One such cooperative mechanism is setting a “cancellation requested” flag that the task checks periodically; if it finds
the flag set, the task terminates early.
There is nothing in the API or language specification that ties interruption to any specific cancellation semantics, but in
practice, using interruption for anything but cancellation is fragile and difficult to sustain in larger applications.
Calling interrupt does not necessarily stop the target thread from doing what it is doing; it merely delivers the
message that interruption has been requested.
A good way to think about interruption is that it does not actually interrupt a running thread; it just requests that the
thread interrupt itself at the next convenient opportunity.
Interruption is usually the most sensible way to implement cancellation.
7.1.2. Interruption Policies
An interruption policy
determines how a thread interprets an interruption request.
A task should not assume anything about the interruption policy of its executing thread unless it is explicitly designed to
run within a service that has a specific interruption policy.
Thread.currentThread().interrupt();
Because each thread has its own interruption policy, you should not interrupt a thread unless you know what
interruption means to that thread.
7.1.3. Responding to Interruption
Only code that implements a thread’s interruption policy may swallow an interruption request. General-purpose task
and library code should never swallow interruption requests.
when you call an interruptible blocking method such as Thread.sleep or
BlockingQueue.put, there are two practical strategies for handling InterruptedException:
x Propagate the exception (possibly after some taskͲspecific cleanup), making your method an interruptible
blocking method, too; or
x Restore the interruption status so that code higher up on the call stack can deal with it.
it shares a
deficiency with join: we don’t know if control was returned because the thread exited normally or because the join
timed out.
ExecutorService.submit (mayInterruptIfRunning)Setting this argument to false means “don’t run this task if it hasn’t started yet”, and should be used for
tasks that are not designed to handle interruption.
it is safe to set mayInterruptIfRunning when
cancelling tasks through their Futures when they are running in a standard Executor.
Future.cancel unconditionally in a finally block, taking advantage of the fact that cancelling a
completed task has no effect.
7.1.4. Example: Timed Run
7.1.5. Cancellation Via Future
ExecutorService.submit:mayInterruptIfRunning:Setting this argument to false means “don’t run this task if it hasn’t started yet”, and should be used for
tasks that are not designed to handle interruption.
The task execution threads created by the standard Executor implementations implement an
interruption policy that lets tasks be cancelled using interruption, so it is safe to set mayInterruptIfRunning when
cancelling tasks through their Futures when they are running in a standard Executor.
Future.cancel unconditionally in a finally block, taking advantage of the fact that cancelling a
completed task has no effect.
7.1.6. Dealing with Non-interruptible Blocking
Many blocking library methods respond to interruption by returning early and throwing InterruptedException, if a thread is blocked performing synchronous socket I/O or waiting to
acquire an intrinsic lock, interruption has no effect other than setting the thread’s interrupted status.
ReaderThread overrides interrupt to both deliver
a standard interrupt and close the underlying socket; thus interrupting a ReaderThread makes it stop what it is doing
whether it is blocked in read or in an interruptible blocking method.
7.1.7. Encapsulating Nonstandard Cancellation with Newtaskfor
7.2-Stopping a thread-based service
start
thread ownership is not transitive: the application may own the service and the
service may own the worker threads, but the application doesn’t own the worker threads and therefore should not
attempt to stop them directly. Instead, the service should provide lifecycle methods for shutting itself down that also
shut down the owned threads; then the application can shut down the service, and the service can shut down the
threads.
Provide lifecycle methods whenever a thread-owning service has a lifetime longer than that of the method that created
it.
7.2.1. Example: A Logging Service
The implementation of log is a check-then-act
sequence: producers could observe that the service has not yet been shut down but still queue messages after the
shutdown, again with the risk that the producer might get blocked in log and never become unblocked.
7.2.2. ExecutorService Shutdown
shutdownNow returns the list of tasks that had not yet
started after attempting to cancel all actively executing tasks.
abrupt termination is faster
but riskier because tasks may be interrupted in the middle of execution, and normal termination is slower but safer
because the ExecutorService does not shut down until all queued tasks are processed.
7.2.3. Poison Pills
poison pill: a recognizable object placed
on the queue that means “when you get this, stop.”
Poison pills work only when the number of producers and consumers is known. each producer place a pill on the queue and having the consumer stop only
when it receives N
producers pills.
7.2.4. Example: A One-shot Execution Service
7.2.5. Limitations of Shutdownnow
The Runnable objects returned by shutdownNow might not be the same objects that were submitted to the ExecutorService: they
might be wrapped instances of the submitted tasks.
7.3. Handling Abnormal Thread Termination
7.3.1. Uncaught Exception Handlers
When a thread exits due to an uncaught exception, the JVM reports this event to an application-provided
UncaughtExceptionHandler; if no handler exists, the default behavior is to print the stack trace to
System.err
In Java 5.0 and later, you
can set an UncaughtExceptionHandler on a per-thread basis with Thread.setUncaughtExceptionHandler, and can also set the
default UncaughtExceptionHandler with Thread.setDefaultUncaughtExceptionHandler.
To set an UncaughtExceptionHandler for pool threads, provide a ThreadFactory to the ThreadPoolExecutor
constructor. If you want to be notified when a
task fails due to an exception so that you can take some task-specific recovery action, either wrap the task with a
Runnable or Callable that catches the exception or override the afterExecute hook in ThreadPoolExecutor.
If a task submitted with submit terminates with an exception, it is rethrown by
Future.get, wrapped in an ExecutionException.
7.4-JVM shutdown
7.4.1. Shutdown Hooks
Shutdown hooks can be used for service or application cleanup, such as deleting temporary files or cleaning up
resources that are not automatically cleaned up by the OS.
7.4.2. Daemon Threads
Threads are divided into two types: normal threads and daemon threads. When the JVM starts up, all the threads it
creates (such as garbage collector and other housekeeping threads) are daemon threads, except the main thread.so by default any threads created by
the main thread are also normal threads.
When the JVM halts, any remaining daemon threads are abandoned
Daemon threads are not a good substitute for properly managing the lifecycle of services within an application.
7.4.3. Finalizers
Since finalizers can run in a thread managed by the JVM, any state accessed by a finalizer will be accessed by more than
one thread and therefore must be accessed with synchronization.
In most cases, the combination of finally blocks and explicit close methods does a better
job of resource management than finalizers; work hard to avoid writing or using classes with finalizers.
Summary
Using FutureTask and the Executor framework
simplifies building cancellable tasks and services.