I am trying to read input from a socket line by line in multiple threads. How can I interrupt readLine() so that I can gracefully stop the thread that it's blocking? EDIT (bounty): Can this be done without closing the socket?
6,841 12 12 gold badges 53 53 silver badges 80 80 bronze badges asked Aug 29, 2010 at 18:03 Jack Edmonds Jack Edmonds 32.8k 18 18 gold badges 67 67 silver badges 78 78 bronze badges Multithreaded stream reading? Isn't that kinda bad to do? Commented Aug 29, 2010 at 18:09@TheLQ: If he is sharing the BufferedReader between the threads and synchronizing the reads, then it's OK. Just there will be no point in doing so.
Commented Aug 29, 2010 at 19:10(Note that using readLine on untrusted input could lead to memory usage comparable to an adversaries bandwidth, leading to a denial-of-service.)
Commented Aug 29, 2010 at 20:57@Denis Tulskiy having for example multiples threads managing sockets with users each with a command terminal, what do you recomend instead of readLine()?
Commented Sep 9, 2011 at 20:55Without closing the socket:
The difficult problem isn't the BufferedReader.readLine , but the underlying read . If a thread is blocked reading, the only way to get it going is to supply some actual data or close the socket (interrupting the thread probably should work, but in practice does not).
So the obvious solution is to have two threads. One that reads the raw data, and will remain blocked. The second, will be the thread calling readLine . Pipe data from the first the second. You then have access to a lock than can be used to wakeup the second thread, and have it take appropriate action.
There are variations. You could have the first thread using NIO, with a single thread instance shared between all consumers.
Alternatively you could write a readLine that works with NIO. This could even take a a relatively simple single-threaded form, as Selector.wakeup exists and works.
answered Sep 17, 2011 at 16:27 Tom Hawtin - tackline Tom Hawtin - tackline 147k 30 30 gold badges 221 221 silver badges 311 311 bronze badgesClose the socket on the interrupting thread. This will cause an exception to be thrown on the interrupted thread.
For more information on this and other concurrency issues, I highly recommend Brian Goetz's book "Java Concurrency in Practice".
answered Aug 29, 2010 at 18:38 Steve Emmerson Steve Emmerson 7,822 6 6 gold badges 34 34 silver badges 59 59 bronze badges Out of curiosity, is it possible to achieve the same effect by closing the BufferedReader itself? Commented Aug 29, 2010 at 18:45 @Jack: yes, closing BufferedReader will close underlying SocketInputStream and the Socket itself. Commented Aug 29, 2010 at 19:08( SocketInputStream is an internal implementation class of some (I believe) but not necessarily all implementations.)
Commented Aug 29, 2010 at 20:55@Denis: My experience is that you can't actually preempt readLine or read on a BufferedReader because it synchronizes read and close on the same monitor. Thus, if blocked waiting for data in read() , calling close() from another thread will just block that thread.
Commented Sep 19, 2013 at 16:18Mark is completely right. There's a deadlock situation that can happen if you try to stop the BufferedReader with close() while it's in the middle of a read() or readLine(). The correct answer is close to socket so it causes the BufferedReader to except.
Commented Apr 29, 2014 at 15:01Sorry for being over 6 years late ;-) I had a need for some interruptible readLine when reading from the keyboard, for a simple hobby console application. In other words, I couldn't "close the socket".
As you may know, System.in is an InputStream that apparently already does some buffering (you need to press Enter ]). However, it seems to be suggested to wrap it in a BufferedReader for better efficiency, so my input is from:
BufferedReader consoleIn = new BufferedReader(new InputStreamReader(System.in));
The other thing one might have discovered is that BufferedReader.readLine() blocks until input is provided (even if the thread is interrupted, which seems to only end the thread once readline() gets its input). It is however possible to predict when BufferedReader.read() will not block, by calling BufferedReader.ready() == true . (However, == false does not guarantee a block, so beware.)
So I have incorporated the above ideas into a method that reads the BufferedReader character by character, checking in between each character if the thread has been interrupted, and also checks for end-of-line, at which point the line of text is returned.
You may find this code useful, pass the consoleIn variable as declared above. (Criticism may be welcomed too. ):
private String interruptibleReadLine(BufferedReader reader) throws InterruptedException, IOException < Pattern line = Pattern.compile("^(.*)\\R"); Matcher matcher; boolean interrupted = false; StringBuilder result = new StringBuilder(); int chr = -1; do < if (reader.ready()) chr = reader.read(); if (chr >-1) result.append((char) chr); matcher = line.matcher(result.toString()); interrupted = Thread.interrupted(); // resets flag, call only once > while (!interrupted && !matcher.matches()); if (interrupted) throw new InterruptedException(); return (matcher.matches() ? matcher.group(1) : ""); >
. And in the thread that is calling this, catch the exceptions and end the thread appropriately.
This was tested in Java 8 on Linux.