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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 02:23:04  来源:igfitidea点击:

How to interrupt BufferedReader's readLine

javamultithreading

提问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 readLinethat works with NIO. This could even take a a relatively simple single-threaded form, as Selector.wakeupexists 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 Foothat uses nonblocking NIO on the socket end but also provides a InputStreamor Readerinterface on the other end. If the BufferedReaderenters its own read, it will call Foo, which will call Selector.selectwith read intent. selectwill either return indicating the presence of more data or it will block until more data is available.

解决方案的草图可能是这样的:NIO 提供了非阻塞 IO 的方法,因此您必须Foo在套接字端实现使用非阻塞 NIO 但在另一端提供InputStreamorReader接口的东西。如果BufferedReader输入它自己的read,它将调用Foo,它将Selector.select以读取意图调用。select要么返回表示存在更多数据,要么阻塞直到有更多数据可用。

If another thread wants to unblock the reader, it must call Selector.wakeupand 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.inis an InputStreamthat apparently already does some buffering (you need to press Enter]). However, it seems to be suggested to wrap it in a BufferedReaderfor better efficiency, so my input is from:

正如您可能知道System.inInputStream那样,显然已经做了一些缓冲(您需要按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, == falsedoes 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 BufferedReadercharacter 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 consoleInvariable 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 readmethods until the BufferedReaderis ready, or a timeout is reached.

在不关闭套接字的情况下,毫无疑问,开销最少的最佳解决方案是readBufferedReader准备好或达到超时之前避免使用阻塞方法。

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 readLineon 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 readLinedoes not return within the specified timeout a SocketTimeoutException is thrown. You can check in a loop whether the ReadingThreadhas been interrupted by the main application, and if so stop reading from the socket.

主应用程序实现了一个服务器套接字,它侦听连接并具有一个线程池。如果传入的客户端通信被接受,则从池中分配一个新线程并调用运行函数ReadingThread(可以调整以允许多个线程)。在用于与客户端通信的套接字上,该属性setSoTimeout(int timeout)已设置。因此,如果readLine在指定的超时时间内没有返回,则抛出 SocketTimeoutException。您可以在循环中检查ReadingThread主应用程序是否已中断,如果是,则停止从套接字读取。