The crucial issue is that the API of System.in makes no guarantees. A JVM can fulfill the complete JVM spec even if it has a System.in
such that, if it is interrupted, nothing happens and it is in fact completely impossible to interrupt System.in, aside from System.exit
.
However, most JVM implementations fortunately don't quite work that way: If you raise the interrupt flag on any given thread, 3 things are going to happen:
Any method that is specced to definitely look at em will be interrupted: These are all methods that are declared to throws InterruptedException
. All these methods will, if the thread's interrupt flag is raised, stop waiting immediately, lower the flag, and return by way of throwing InterruptedException
. Yes, this means that if you first raise the interrupt flag (someThread.interrupt()
raises the flag and doesn't do anything else; it's other methods that look at it that makes the magic work), and then invoke e.g. Thread.sleep
, the sleep calls returns immediately (by throwing InterruptedEx) and waits no even a single millisecond.
Methods that pause a thread but which are not specced to definitely deal with it properly are in limboland: It is up to the implementation of the java runtime if anything happens. However, usually something will happen. These methods almost always throw some sort of checked exception (for DB connections, SQLEx
, for network, file, and pipe operations, IOException
); any code that is currently waiting to send or receive data on one of these things will deal with a raised interrupt flag by lowering the flag, aborting the operation, and returning by way of throwing that checked exception with a message that indicates an interruption occurred.
If code is executing that doesn't respond to the interrupt flag at all, then nothing happens: The flag stays raised and the JVM is not going to do anything else; the point of the interrupt flag is that it just gets raised and then you wait until the thread runs code that looks at it. Hopefully, that will happen very soon, but there are no guarantees.
That means that most likely all you need to do is:
In T1
- Have some sort of AtomicBoolean object that will be set to
true
by t1 once the job is completed.
- t1 will also raise the interrupt flag of t2 when the job is completed.
In T2
Protect your readLine() call by putting it in a try/catch block, catching IOException. If there is a loop you may also want to consider checking the interrupt flag yourself, in case it is set in between readLine() invokes; you do this with Thread.interrupted()
, which returns true and lowers the flag if the flag is up. Generally, something like while (!Thread.interrupted() && other conditions) { /* main loop here */ }
.
In the IOException catch handler, check t1's 'we are done' flag (that AtomicBoolean). If it says 'we are done', then interpret the IOEx as simply being notified that the job is done (so, don't log it anywhere - you were expecting it to happen). If, however, the 'we are done' flag isn't set yet, then that IOException is indicating an actual I/O problem with the input pipe, which can happen of course. You should proceed as normal (which usually means, throw it onwards so that the app crashes with a full log, you can't sanely respond to the input pipe getting I/O issues other than to exit with debug info about what happend). So, just throw that IOException. If you can't, throw new UncheckedIOException(thatIoException);
is what you are looking for.
The caveat
Just because it works on your system does not mean it will work anywhere else, unfortunately. As I said, on some VM impls System.in.read()
is just not interruptable, period. Nothing you can do, other than extremely drastic steps: Stop being a command line app and show a swing GUI window instead or make it a web app of some sort.
Closing notes
ready()
and available()
are almost completely useless. They aren't broken, in the sense that they do exactly what their javadoc says these methods do, but if you carefully read that javadoc, you'll realize that what they provide is completely useless. The only real way to know if data is available is to actually attempt to read it, which then leads you into the trap of: Well, on some platforms, that's not interruptable. Yup. Sucks. No reliable solution, in the sense that the API guarantees it'll work on all platforms, is available. 99.5% of all code out there that calls these methods is broken. It is highly unlikely that you'd ever want to call these methods.