Related
I would like to shutdown a thread gracefully. However, once the shutdown is initiated the thread should perform some shutdown operation after ending usual operation.
Both threads use sleeps and/or wait and handle InterruptedException, they also work on tasks in a loop taking only a few milliseconds. So that I expected the while loop to end because Thread.currentThread().isInterrupted() becomes "true".
The problem is that with my code sometimes I get the log "SHUTDOWN" and sometimes not. Also I get "INTERRUPTED" only sometimes, which I understand of course. With another similar thread I never get the "SHUTDOWN".
ExecutorService executor = Executors.newFixedThreadPool(2);
executor.execute(new Test());
Thread.sleep(10000);
executor.shutdown();
try {
if(this.executor.awaitTermination(60, TimeUnit.SECONDS)) {
this.loggerFactory.getLogger(this.getClass()).info("CLOSED (GRACEFULLY)!");
} else {
this.executor.shutdownNow();
this.loggerFactory.getLogger(this.getClass()).info("CLOSED (IMMEDIATELY)!");
}
} catch(InterruptedException e) {
this.executor.shutdownNow();
this.loggerFactory.getLogger(this.getClass()).info("CLOSED (IMMEDIATELY)!");
}
class Test implements Runnable {
private volatile boolean isRunning = true;
#Override
public void run() {
try {
while(!Thread.currentThread().isInterrupted()) {
while(!this.isRunning) {
synchronized(this) {
this.wait();
}
}
// DO SOMETHING LASTING A FEW MILLISECONDS
Thread.sleep(500);
}
} catch(InterruptedException e) {
this.loggerFactory.getLogger(this.getClass()).info("INTERRUPTED!");
}
this.loggerFactory.getLogger(this.getClass()).info("SHUTDOWN!");
// DO SOME SHUTDOWN OPERATION
}
}
EDIT:
After some commentary by OP, an entirely different and much superior solution seems to be available:
Use hooks!
Java has a system to 'install' a shutdown hook. These are called when the VM shuts down... sometimes. If you get SIGTERMed (kill -9) or someone trips over a powercable, or linux kills your process due to excessive memory use, or the kernel dumps, or your VM hard crashes (for example, a core dump in native code), or the device loses power, they don't get called, of course.
But, if someone in the process runs System.exit(), or all non-daemon threads are done, or someone hits CTRL+C or sends SIGKILL (kill, not kill -9) to your process, they get run first, and only when they all finish does the java process actually end.
That sounds like a vastly superior solution here. Your shutdown hook should:
acquire the lock on some private AtomicBoolean.
set the boolean to false (the boolean indicates: May I query this sensor?)
release the lock.
reset the sensor.
return.
And all your normal operation code that reads that sensor should:
acquire a lock on the boolean.
if false, throw or otherwise abort.
perform the sensor read operation.
release the lock.
Nothing should ever touch that sensor without holding the lock (failure to do this would imply maybe messing with that sensor after you've already reset it, which would be bad).
original answer:
I would like to shutdown a thread gracefully.
Why? 'gracefully' is a very nice sounding word, but once you dig into what it means, it's just nasty things. It's a word that means: "That will cause my software to fail, possibly persistently (as in, won't start anymore without cleaning up stuff), if someone trips over a powercable or my app hard-crashes".
A much better design is to have a thread that doesn't need to be shut down. Just pull the plug on it, and all is well.
For example, old filesystems (MS-DOS and early windows age) required graceful shutdowns; failure to do so would lead to persistent issues - the system wouldn't boot at all, you bricked the box. They then had mitigation systems in place (chkdsk systems), but modern OSes are much better. Their filesystem handling setup mostly doesn't care about being 'gracefully' shut down. Just pull the plug on em, they'll be fine, that's what journals do.
So that I expected the while loop to end because Thread.currentThread().isInterrupted() becomes "true".
That's not how you're supposed to use that API.
Here's the basic gist of what the interrupted API does:
Any thread can 'raise the interrupt flag' on any other (someThread.interrupt()).
raising the flag doesn't do anything other than raise the flag, unless a method explicitly decides to look at it.
The method Thread.interrupted() is how you're supposed to read the flag out in order to act upon it, __and not Thread.currentThread().isInterrupted(). The former will check the flag and clear it. The latter merely checks the flag.
Some java methods are specced to respond to the flag being up. You recognize these methods because they throws InterruptedException. There may be more methods; for example, on most OSes, interrupting a thread currently waiting for more bytes to flow in from the network (they are blocked on a read() call on an InputStream obtained from socket.getInputStream()) WILL cause that read call to fail (with an IOException, not an InterruptedException, because read() isn't specced to throw InterruptedEx), but that's no guarantee; on some OSes, it won't, and you can't interrupted that.
The general policy is that the moment you handle an interrupted flag, you lower the flag, and java code does just that: If a method throws InterruptedEx, the flag will be cleared.
Java does not define what you should do if interrupted. Threads don't get magically interrupted; for example, when your VM shuts down (someone hits CTRL+C), that doesn't interrupt any threads whatsoever. Java will just 'pull the plug' on all threads. That's because this is better (see above). Therefore, if a thread is interrupted, that's because you wrote thread.interrupt() someplace, therefore, you decide what it means. Maybe it means 're-read a config file and restart the server listening process'. Maybe it means 'stop calculating chess moves and perform the best move found so far'. Maybe it means 'recheck for a condition'. Maybe it means 'end the thread entirely'. It's up to you. There is no standard.
Note that the various methods specced to respond to interrupt flags (such as wait(): It throws InterruptedException) all share this property: If you call them while the flag is up, they will instantly return by throwing InterruptedException, they never even begin waiting.
So, for your code, given that you wait() already, just make that while(true) and rely on the InterruptedEx.
I've come across the code below, and I'm wondering if it does exactly what I think it does:
synchronized(sObject) {
mShouldExit = true;
sObject.notifyAll()
while (!mExited) {
try {
sObject.wait();
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
}
About the context: there is another thread that checks for mShouldExit (inside the sObject monitor) and will exit in that case.
This does not look to be a correct pattern to me. If an interrupt happens, it will set the interrupted status again, so when it returns to sObject.wait(), another InterruptedException will come etc. etc. etc. Therefore, it can never go to truly waiting state (sObject.wait()) i.e. it will never release the sObject monitor. This may result in an infinite loop, as the other thread cannot set mExiting to true, because it can never enter sObject's monitor. (So I think that the interrupt() call is an error, it must not be used here.) Am I missing something?
Note that the code snippet is a part of the official Android framework source code.
UPDATE: actually, the situation is worse, because the same pattern is used in Android when your GL rendering starts. The official source code of GLSurfaceView.GLThread.surfaceCreated():
public void surfaceCreated() {
synchronized(sGLThreadManager) {
if (LOG_THREADS) {
Log.i("GLThread", "surfaceCreated tid=" + getId());
}
mHasSurface = true;
sGLThreadManager.notifyAll();
while((mWaitingForSurface) && (!mExited)) {
try {
sGLThreadManager.wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
You can reproduce the bug in a similar way: make sure your UI thread has its interrupted status flag yet, then add your GLSurfaceView and start the GL rendering (via setRenderer(...), but on some devices, make sure your GLSurfaceView has Visibility.VISIBLE status, otherwise rendering will not start).
If you follow the above steps, your UI thread will end up in an infinite loop, because the above-quoted code will keep generating an InterruptedException (due to wait()) and therefore the GL thread will never be able to set mWaitingForSurface to false.
According to my tests, it seems that such an infinite loop will also result in an endless sequence of GC_CONCURRENT garbage collection (or, at least, such messages in logcat). Interesting, someone had an unknown poorly-defined issue on stackoverflow earlier which might be related:
How to solve GC_concurrent freed?
Isn't it possible that perhaps his UI thread had its interrupted flag set to true, and he was using a GLSurfaceView for the map he mentions? Just an assumption, a possible scenario.
Short version: That code is wrong, and will cause an infinite loop (I still have a doubt, but may depend on JVM implementations). Setting the interrupt status is the right thing to do, but it should then exit the loop, eventually checking that same interruption status using Thread.isInterrupted().
Long version for the casual reader:
The problem is how to stop a thread that is currently executing some work, in response to a "Cancel" button from the user or because of some other application logic.
Initially, Java supported a "stop" method, that preemptively stopped a thread. This method has been demonstrated to be unsafe, cause didn't give the stopped thread any (easy) way to clean up, release resources, avoid exposing partially modified objects and so on.
So, Java evolved to a "cooperative" Thread "interruption" system. This system is quite simple : a Thread is running, someone else calls "interrupt" on it, a flag is set on the Thread, it's Thread responsibility to check if it has been interrupted or not and act accordingly.
So, correct Thread.run (or Runnable.run, of Callable etc..) method implementation should be something like :
public void run() {
while (!Thread.getCurrentThread().isInterrupted()) {
// Do your work here
// Eventually check isInterrupted again before long running computations
}
// clean up and return
}
This is fine as long as all the code your Thread is executing is inside your run method, and you never call something that blocks for a long time ... which is often not the case, cause if you spawn a Thread is because you have something long to do.
The simplest method that block is Thread.sleep(millis), it's actually the only thing it does : it blocks the thread for the given amount of time.
Now, if the interrupt arrives while your thread is inside Thread.sleep(600000000), without any other suport, it would take a lot for it to arrive to the point where it checks isInterrupted.
There are even situations where your thread would never exit. For example, your thread is computing something and sending results to a BlockingQueue with a limited size, you call queue.put(myresult), it will block until the consumer free some space in the queue, if in the mean time the consumer has been interrupted (or died or whatever), that space will never arrive, the method will not return, the check on .isInterrupted will never be performed, your thread is stuck.
To avoid this situation, all (most) methods that interrupt the thread (should) throw InterruptedException. That exception simply tells you "I was waiting for this and that, but in the meanwhile the thread as been interrupted, you should do cleanup and exit as soon as possible".
As with all exceptions, unless you know what to do, you should re-throw it and hope that someone above you in the call stack knows.
InterruptedExceptions are even worse, since when they are thrown the "interrupted status" is cleared. This means that simply catching and ignoring them will result in a thread that usually does not stop :
public void run() {
while (!Thread.getCurrentThread().isInterrupted()) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// Nothing here
}
}
}
In this example, if the interrupt arrives during the sleep() method (which is 99.9999999999% of the time), it will throw InterruptedException, clear the interrupt flag, then the loop will continue since the interrupt flag is false, and the thread will not stop.
That's why if you implement your "while" correctly, using .isInterrupted, and you really need to catch InterruptedException, and you don't have anything special (like cleanup, return etc..) to do with it, least that you can do is set the interrupt flag again.
The problem in the code you posted is that the "while" relies solely on mExited to decide when to stop, and not ALSO on isInterrupted.
while (!mExited && !Thread.getCurrentThread().isInterrupted()) {
Or it could exit when interrupted :
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return; // supposing there is no cleanup or other stuff to be done
}
Setting the isInterrupted flag back to true is also important if you don't control the Thread. For example, if you are in a runnable which is being executed in a thread pool of some kind, or inside any method anywhere you don't own and control the thread (a simple case : a servlet), you don't know if the interruption is for "you" (in the servlet case, the client closed the connection and the container is trying to stop you to free the thread for other requests) or if it's targeted at the thread (or system) as a whole (the container is shutting down, stopping everything).
In that situation (which is 99% of the code), if you cannot rethrow the InterruptedException (which is, unfortunately, checked), the only way to propagate up the stack to the thread pool that the thread has been interrupted, is setting the flag back to true before returning.
That way, it will propagate up the stack, eventually generating more InterruptedException's, up to the thread owner (be it the jvm itself, of an Executor, or any other thread pool) that can react properly (reuse the thread, let it die, System.exit(1) ...)
Most of this is covered in chapter 7 of Java Concurrency in Practice, a very good book that I recommend to anyone interested in computer programming in general, not just Java, cause the problems and the solutions are similar in many other environments, and explanations are very well written.
Why Sun decided to make InterruptedException checked, when most documentation suggests to rethrow it mercilessly, and why they decided to clear the interrupted flag when throwing that exception, when the proper thing to do is setting it to true again most of the time, remains open for debate.
However, if .wait releases the lock BEFORE checking for the interrupt flag, it open a small door from another thread to modify the mExited boolean. Unfortunately the wait() method is native, so source of that specific JVM should be inspected. This does not change the fact that the code you posted is coded poorly.
I am wondering if a typical while(true) ServerSocket listen loop takes an entire core to wait and accept a client connection (Even when implementing runnable and using Thread .start())
I am implementing a type of distributed computing cluster and each computer needs every core it has for computation. A Master node needs to communicate with these computers (invoking static methods that modify the algorithm's functioning).
The reason I need to use sockets is due to the cross platform / cross language capabilities. In some cases, PHP will be invoking these java static methods.
I used a java profiler (YourKit) and I can see my running ServerSocket listen thread and it never sleeps and it's always running. Is there a better approach to do what I want? Or, will the performance hit be negligible?
Please, feel free to offer any suggestion if you can think of a better way (I've tried RMI, but it isn't supported cross-language.
Thanks everyone
If you mean something like this:
while (true) {
Socket socket = server.accept();
/* Do something with socket... */
}
then, no, the call to accept() does not "take an entire core." It's a blocking call that will allow the CPU to be scheduled for another thread until a client actually connects. Once the call to accept() returns, the current thread will be scheduled to run and will consume CPU until it blocks on accept() in the next iteration of the loop.
To avoid the listen backlog of other clients growing too large, another thread usually handles interaction with the newly-accepted Socket, leaving one thread to focus on accepting new clients. The socket handling thread might handle many sockets, using NIO, or it might be dedicated to a single socket, which is much simpler to code but won't scale well past many hundreds of simultaneous connections.
You might want to take a look at the Java 1.4 nio libraries and in particular ServerSocketChannel. I use this very successfully to implement an efficient server, the key bits of code are:
Selector selector = Selector.open();
ServerSocketChannel server= ServerSocketChannel.open();
server.socket().bind(new java.net.InetSocketAddress(port));
server.configureBlocking(false);
SelectionKey serverKey = server.register(selector, SelectionKey.OP_ACCEPT);
// start listening thread
new Thread(listener).start();
And the listener is just a loop that runs:
selector.select(1000); // listen for one second max
Set<SelectionKey> keys = selector.selectedKeys();
if (keys.size()>0) {
handleKeys(keys);
}
I used a java profiler (YourKit) and I can see my running ServerSocket listen thread and it never sleeps and it's always running.
Basically, the profiler is misleading you.
I assume your code looks like this:
ServerSocket server = ...
// configure server socket
try {
while (true) {
Socket socket = server.accept();
// do something with socket (and close it afterwards!)
}
} catch (InterruptedException ex) {
// we're outta here!
}
This will not consume significant CPU ... unless you have done something pathological, like calling ServerSocket.setSoTimeout(int) with a small timeout.
Let the core sleep a little. In your Runnable method, add something like
Thread.sleep(250); // milliseconds
in every loop.
That should significantly reduce CPU usage
Edit: bad idea, see comments, sorry, my fault
And: don't use while(true). it's awful design, as the semantics suggest that eventually true will not be true anymore. Usually you will want to query some volatile or atomic variable from the main thread
public class MyClass {
class MyRunnable implements Runnable {
public void run() {
while (MyClass.this.keepGoing.get()) {
// listen();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// deal with exception
}
}
}
}
private final AtomicBoolean keepGoing = new AtomicBoolean(true);
}
That way the main thread has a way to stop the listener thread.
A teammate made the following claim:
"Thread.interrupt() is inherently broken, and should (almost) never be used".
I am trying to understand why this is the case.
Is it a known best practice never to use Thread.interrupt()? Can you provide evidence why it is broken / buggy, and should not be used for writing robust multithreaded code?
Note - I am not interested in this question if it's "pretty" from a design preservative. My question is - is it buggy?
Short version:
Is it a known best practice never to
use Thread.interrupt()?
No.
Can you provide
evidence why it is broken / buggie,
and should not be used for writing
robust multithreaded code?
The opposite is true: it is critical for multithreaded code.
See Listing 7.7 in Java Concurrency in Practice for an example.
Longer version:
Around here, we use this method in one specific place: handling InterruptedExceptions. That may seem a little strange but here's what it looks like in code:
try {
// Some code that might throw an InterruptedException.
// Using sleep as an example
Thread.sleep(10000);
} catch (InterruptedException ie) {
System.err.println("Interrupted in our long run. Stopping.");
Thread.currentThread().interrupt();
}
This does two things for us:
It avoids eating the interrupt exception. IDE auto-exception handlers always provide you with something like ie.printStackTrace(); and a jaunty "TODO: Something useful needs to go here!" comment.
It restores the interrupt status without forcing a checked exception on this method. If the method signature that you're implementing does not have a throws InterruptedException clause, this is your other option for propagating that interrupted status.
A commenter suggested that I should be using an unchecked exception "to force the thread to die." This is assuming that I have prior knowledge that killing the thread abruptly is the proper thing to do. I don't.
To quote Brian Goetz from JCIP on the page before the listing cited above:
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.
For example, imagine that I did this:
} catch (InterruptedException ie) {
System.err.println("Interrupted in our long run. Stopping.");
// The following is very rude.
throw new RuntimeException("I think the thread should die immediately", ie);
}
I would be declaring that, regardless of other obligations of the rest of the call stack and associated state, this thread needs to die right now. I would be trying to sneak past all the other catch blocks and state clean-up code to get straight to thread death. Worse, I would have consumed the thread's interrupted status. Upstream logic would now have to deconstruct my exception to try to puzzle out whether there was a program logic error or whether I'm trying to hide a checked exception inside an obscuring wrapper.
For example, here's what everyone else on the team would immediately have to do:
try {
callBobsCode();
} catch (RuntimeException e) { // Because Bob is a jerk
if (e.getCause() instanceOf InterruptedException) {
// Man, what is that guy's problem?
interruptCleanlyAndPreserveState();
// Restoring the interrupt status
Thread.currentThread().interrupt();
}
}
The interrupted state is more important than any specific InterruptException. For a specific example why, see the javadoc for Thread.interrupt():
If this thread is blocked in an invocation of the wait(), wait(long),
or wait(long, int) methods of the Object class, or of the join(),
join(long), join(long, int), sleep(long), or sleep(long, int), methods
of this class, then its interrupt status will be cleared and it will
receive an InterruptedException.
As you can see, more than one InterruptedException could get created and handled as interrupt requests are processed but only if that interrupt status is preserved.
The only way I'm aware of in which Thread.interrupt() is broken is that it doesn't actually do what it seems like it might - it can only actually interrupt code that listens for it.
However, used properly, it seems to me like a good built-in mechanism for task management and cancellation.
I recommend Java Concurrency in Practice for more reading on the proper and safe use of it.
The main problem with Thread.interrupt() is that most programmers don't know about the hidden pitfalls and use it in the wrong way. For example, when you handle the interrupt, there are methods which clear the flag (so the status gets lost).
Also, the call will not always interrupt the thread right away. For example, when it hangs in some system routine, nothing will happen. In fact, if the thread doesn't check the flag and never calls a Java method which throws InterruptException, then interrupting it will have no effect whatsoever.
No, it's not buggy. It actually is the basis of how you stop threads in Java. It's used in the Executor framework from java.util.concurrent - see the implementation of java.util.concurrent.FutureTask.Sync.innerCancel.
As for failure, I've never seen it fail, and I've used it extensively.
One reason not mentioned is that the interrupt signal can be lost which makes invoking the Thread.interrupt() method meaningless. So unless your code in the Thread.run() method is spinning in a while loop the outcome of calling Thread.interrupt() is uncertain.
I noticed that when in thread ONE I execute DriverManager.getConnection() when there is no database connection available (say server is down thus finally this line throws SQLException) and from the thread TWO I explicitely call ONE.interrupt(), then both ONE.interrupted() and ONE.isInterrupted() return false even if placed as the first line in the catch{} block where SQLException is handled.
Of course I workarounded this issue implementing the extra semaphore but it is quite troublesome, as it is the very first such issue in my 15 years Java development.
I suppose it's because of bug in com.microsoft.sqlserver.jdbc.SQLServerDriver. And I investigated more to confirm that the call to the native function consumes this interruption in all cases it trhows its own, but preserves it when succeeded.
Tomek
P.S. I found the analogous issue.
P.P.S
I enclose a very short example of what I'm writting above. The registered class can be found in sqljdbc42.jar. I found this bug in classes built on 2015-08-20 then I updated to the newest version available (from 2017-01-12) and the bug still exists.
import java.sql.*;
public class TEST implements Runnable{
static{
try{
//register the proper driver
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
}
catch(ClassNotFoundException e){
System.err.println("Cannot load JDBC driver (not found in CLASSPATH).");
}
}
public void run() {
Thread.currentThread().interrupt();
System.out.println(Thread.currentThread().isInterrupted());
//prints true
try{
Connection conn = DriverManager.getConnection("jdbc:sqlserver://xxxx\\xxxx;databaseName=xxxx;integratedSecurity=true");
}
catch (SQLException e){
System.out.println(e.getMessage());
}
System.out.println(Thread.currentThread().isInterrupted());
//prints false
System.exit(0);
}
public static void main(String[] args){
(new Thread(new TEST())).start();
}
}
If you pass something completely incorrect, as "foo", to the DriverManager.getConnection(), you will obtain the message "No suitable driver found for foo", and the second printout will be still true as one would expect. But if you pass the correctly built string but, say, your server is down or you lost your net connection (that can generally occurr in the production environment), you will see the java.net socket timeout error printout and the thread's interrupted() state is LOST.
THe problem is not that the implementation is not buggy but rather your thread is in an unknown state when it gets interrupted and this can lead to unpredictable behavior.
I have a long running task, something like:
public void myCancellableTask() {
while ( someCondition ) {
checkIfCancelRequested();
doSomeWork();
}
}
The task can be cancelled (a cancel is requested and checkIfCancelRequested() checks the cancel flag). Generally when I write cancellable loops like this, I use a flag to indicate that a cancel has been requested. But, I know I could also use Thread.interrupt and check if the thread has been interrupted. I'm not sure which would be the preferred approach and why, thoughts?
thanks,
Jeff
One problem with using interrupt is that if you do not control all code being executed, you run the risk of the interrupt not working "properly" because of someone else's broken understanding of how to handle interrupts in their library. That is the API invisibly exports an API around its handling of interrupts which you become dependent on.
In your example, suppose doSomeWork was in a 3rd-party JAR and looks like:
public void doSomeWork() {
try {
api.callAndWaitAnswer() ;
}
catch (InterruptedException e) { throw new AssertionError(); }
}
Now you have to handle an AssertionError (or whatever else the library you are using might throw). I've seen experienced developers throw all sorts of nonsense on receiving interrupts! On the other hand, maybe the method looked like this:
public void doSomeWork() {
while (true) {
try {
return api.callAndWaitAnswer() ;
}
catch (InterruptedException e) { /* retry! */ }
}
}
This "improper handling" of interrupt causes your program to loop indefinitely. Again, don't dismiss this as ridiculous; there are a lot of broken interrupt handling mechanisms out there.
At least using your own flag will be completely invisible to any 3rd-party libraries.
Interrupt will blast the thread out of a list of specified wait conditions. Your own cancel flag will not. If you want to interrupt waits on IO and events, use interrupt. Otherwise use your own.
It depends on the doSomeWork() implementation. Is that pure computation or does it (at any point) involve blocking API (such as IO) calls? Per bmargulies's answer, many blocking APIs in JDK are interruptible and will propagate the interrupted exception up the stack.
So, if the work entails potentially blocking activities, you need'll to take interrupts into consideration even if you decide to control the process using a flag, and should appropriately catch and handle/propagate the interrupts.
Beyond that, if relying on a flag, make sure your flag is declared with volatile semantics.
I think that it's a matter of preference in most cases.
Personally I would go for the hand-made flag. It gives you more control - for example, this way you make sure that your thread doesn't leave some other object in an inconsistent state.
Besides, if performance is really critical, bear in mind that using exceptions has an overhead (even if it's negligible in 99% of the cases).
First, let's take a look at the usage conditions.
If we have a thread pool and use interruption as the cancellation mechanism, we can only interrupt the worker threads through the pool. In other words, we can't directly invoke Thread.interrupt since we don't own the threads. So, we must acquire a Future and invoke Future.cancel. Or we must call ExecutorService.shutdownNow to cancel all tasks interrupting the busy threads. In the first case, it requires some bookkeeping on our side to hold the Future handles. So the application must keep new tasks and remove the old ones.
On the other hand, if you use a global cancellation flag, we can cancel/stop multiple tasks from a central place without additional bookkeeping. But if we want to cancel an individual task - similar to invoking Future.cancel - we must store a cancellation flag for each task.
Secondly, let's examine the general convention.
Java class libraries generally interpret a thread interrupt as a cancellation request. For example, LinkedBlockingQueue.take can make our program block. But when the current thread is interrupted it throws an InterruptedException and returns. So our application becomes responsive by using a responsive method. So we can just build upon the already existing support and write additional Thread.interrupted/Thread.currentThread().isInterrupted checks in our code.
Moreover, the cancellation methods in ExecutorService use thread interruption. As we mentioned, Future.cancel and ExecutorService.shutdownNow rely on interrupts.