Java 如何中断 BufferedReader 的 readLine
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3595926/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to interrupt BufferedReader's readLine
提问by Hyman Edmonds
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?
我正在尝试在多个线程中逐行读取来自套接字的输入。如何中断readLine()
以便我可以优雅地停止它阻塞的线程?
EDIT (bounty): Can this be done without closing the socket?
编辑(赏金):这可以在不关闭套接字的情况下完成吗?
采纳答案by Steve Emmerson
Close 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".
关于这个和其他并发问题的更多信息,我强烈推荐 Brian Goetz 的书“Java Concurrency in Practice”。
回答by Michael
I think that you might have to use something other than readLine()
. You could use read()
and at every loop iteration check to see if the thread was interrupted and break out of the loop if it was.
我认为您可能必须使用readLine()
. 您可以read()
在每次循环迭代中使用和 检查线程是否被中断,如果中断则退出循环。
BufferedReader reader = //...
int c;
while ((c = reader.read()) != -1){
if (Thread.isInterrupted()){
break;
}
if (c == '\n'){
//newline
}
//...
}
回答by mhshams
you can design a Timer class around the read() block.
您可以围绕 read() 块设计一个 Timer 类。
you need to set a timeout for your timer.
您需要为计时器设置超时。
on timeout just interrupt your thread.
超时只是中断你的线程。
回答by Tom Hawtin - tackline
Without 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).
困难的问题不是BufferedReader.readLine
,而是底层的read
. 如果线程被阻塞读取,唯一的方法是提供一些实际数据或关闭套接字(中断线程可能应该起作用,但实际上不会)。
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.
所以显而易见的解决方案是有两个线程。一种读取原始数据,并将保持阻塞状态。第二个,将是线程调用readLine
. 管道数据从第一个到第二个。然后,您可以访问可用于唤醒第二个线程的锁,并让它采取适当的操作。
There are variations. You could have the first thread using NIO, with a single thread instance shared between all consumers.
有变化。您可以使用 NIO 拥有第一个线程,并在所有使用者之间共享一个线程实例。
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.
或者,您可以编写一个readLine
适用于 NIO 的程序。这甚至可以采用相对简单的单线程形式,如Selector.wakeup
存在且有效。
回答by A.H.
A sketch for a solution might be this: NIO provides methods for nonblocking IO, so you have to implement something called Foo
that uses nonblocking NIO on the socket end but also provides a InputStream
or Reader
interface on the other end. If the BufferedReader
enters its own read
, it will call Foo
, which will call Selector.select
with read intent. select
will either return indicating the presence of more data or it will block until more data is available.
解决方案的草图可能是这样的:NIO 提供了非阻塞 IO 的方法,因此您必须Foo
在套接字端实现使用非阻塞 NIO 但在另一端提供InputStream
orReader
接口的东西。如果BufferedReader
输入它自己的read
,它将调用Foo
,它将Selector.select
以读取意图调用。select
要么返回表示存在更多数据,要么阻塞直到有更多数据可用。
If another thread wants to unblock the reader, it must call Selector.wakeup
and the selector can return gracefully by throwing an exception the by BufferedReader
.
如果另一个线程想要解除对读取器的阻塞,它必须调用Selector.wakeup
并且选择器可以通过抛出异常来优雅地返回 by BufferedReader
。
The socket should be still open after that.
之后套接字应该仍然打开。
Variation A: call Selector.select(timeout)
to do busy polling light.
变化A:呼叫Selector.select(timeout)
做忙轮询灯。
回答by bnsmith
I was playing around with this recently (using Scala), and I didn't like the accepted answer of closing the socket and getting an exception.
我最近在玩这个(使用 Scala),我不喜欢关闭套接字并获得异常的公认答案。
Eventually I discovered that it's possible to call socket.shutdownInput()
in the interrupting thread to get out of the readLine call without an exception. I make this call in a SIGINT handler so that I can clean up and close the socket in the main thread.
最终我发现可以 socket.shutdownInput()
在中断线程中调用以无异常地退出 readLine 调用。我在 SIGINT 处理程序中进行此调用,以便我可以清理和关闭主线程中的套接字。
Note, that the equivalent exists for the outputstream with socket.shutdownOutput()
请注意,输出流的等效项存在 socket.shutdownOutput()
回答by fr13d
Sorry 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".
很抱歉迟到了 6 年以上;-) 我需要一些可中断的 readLine 从键盘上阅读时需要一个简单的爱好控制台应用程序。换句话说,我无法“关闭套接字”。
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:
正如您可能知道System.in
的InputStream
那样,显然已经做了一些缓冲(您需要按Enter])。但是,似乎建议将其包装在 a 中BufferedReader
以提高效率,因此我的输入来自:
BufferedReader consoleIn = new BufferedReader(new InputStreamReader(System.in));
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.)
人们可能发现的另一件事BufferedReader.readLine()
是在提供输入之前阻塞(即使线程被中断,这似乎只在线程readline()
获得输入后才结束)。不过,可以预测何时BufferedReader.read()
将不会阻止,通过调用BufferedReader.ready() == true
。(但是,== false
并不能保证一个块,所以要小心。)
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.
所以我把上面的想法融入到一个BufferedReader
逐个字符读取的方法中,在每个字符之间检查线程是否被中断,并且还检查行尾,此时返回文本行。
You may find this code useful, pass the consoleIn
variable as declared above. (Criticism may be welcomed too...):
您可能会发现此代码很有用,请consoleIn
按照上面声明的方式传递变量。(也欢迎批评……):
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.
这是在 Linux 上的 Java 8 中测试的。
回答by Cord Rehn
Without closing the socket, no question the best solution with the least overhead is to simply avoid using the blocking read
methods until the BufferedReader
is ready, or a timeout is reached.
在不关闭套接字的情况下,毫无疑问,开销最少的最佳解决方案是read
在BufferedReader
准备好或达到超时之前避免使用阻塞方法。
public String readLineTimeout(BufferedReader reader, long timeout) throws TimeoutException, IOException {
long start = System.currentTimeMillis();
while (!reader.ready()) {
if (System.currentTimeMillis() - start >= timeout)
throw new TimeoutException();
// optional delay between polling
try { Thread.sleep(50); } catch (Exception ignore) {}
}
return reader.readLine(); // won't block since reader is ready
}
回答by Bernhard_S
If you want to use readLine
on a server socket within a client-server tcp architecture, for instance, you can use setSoTimeout(int timeout)
of java.net.Socket
.
如果你想使用readLine
一个客户端-服务器的TCP架构内的服务器套接字,例如,你可以使用setSoTimeout(int timeout)
的java.net.Socket
。
From the Socket#setSoTimeout(int timeout) Documentation:
来自Socket#setSoTimeout(int timeout) 文档:
Enable/disable SO_TIMEOUT with the specified timeout, in milliseconds. With this option set to a non-zero timeout, a read() call on the InputStream associated with this Socket will block for only this amount of time. If the timeout expires, a java.net.SocketTimeoutExceptionis raised, though the Socket is still valid.
使用指定的超时时间(以毫秒为单位)启用/禁用 SO_TIMEOUT。将此选项设置为非零超时,与此 Socket 关联的 InputStream 上的 read() 调用将仅阻塞此时间量。如果超时到期,则会引发java.net.SocketTimeoutException,尽管 Socket 仍然有效。
public class MainApp {
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newFixedThreadPool(10);
ServerSocket serverSocket = new ServerSocket(11370);
Socket clientSocket = serverSocket.accept();
clientSocket.setSoTimeout(2000);
executorService.execute(new ReadingThread(clientSocket));
// ... some async operations
executorService.shutdown();
}
}
public class ReadingThread implements Runnable {
private final Socket clientSocket;
public ReadingThread(Socket clientSocket) {
this.clientSocket = clientSocket;
}
@Override
public void run() {
BufferedReader socketReader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
String readInput = null;
while (!Thread.currentThread().isInterrupted()) {
try {
readInput = socketReader.readLine();
} catch (SocketTimeoutException e) {
continue;
}
}
// operations with readInput
}
}
The main application implements a server socket which listens to connections and has a thread pool. If an incoming client communication is accepted, then a new Thread from the pool is assigned and the run function is invoked in ReadingThread
(can be adjusted to allow multiple threads).
On the socket used for communicating to the client the property setSoTimeout(int timeout)
has been set. Therefore if readLine
does not return within the specified timeout a SocketTimeoutException is thrown.
You can check in a loop whether the ReadingThread
has been interrupted by the main application, and if so stop reading from the socket.
主应用程序实现了一个服务器套接字,它侦听连接并具有一个线程池。如果传入的客户端通信被接受,则从池中分配一个新线程并调用运行函数ReadingThread
(可以调整以允许多个线程)。在用于与客户端通信的套接字上,该属性setSoTimeout(int timeout)
已设置。因此,如果readLine
在指定的超时时间内没有返回,则抛出 SocketTimeoutException。您可以在循环中检查ReadingThread
主应用程序是否已中断,如果是,则停止从套接字读取。