Java:如何中止从 System.in 读取的线程
我有一个 Java 线程:
class MyThread extends Thread {
@Override
public void run() {
BufferedReader stdin =
new BufferedReader(new InputStreamReader(System.in));
String msg;
try {
while ((msg = stdin.readLine()) != null) {
System.out.println("Got: " + msg);
}
System.out.println("Aborted.");
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
}
在另一个线程中,如何中止该线程中的 stdin.readline()
调用,以便该线程打印 Aborted.
?我已经尝试过 System.in.close() ,但这没有任何区别,stdin.readline() 仍然阻塞。
我对无需
- 忙于等待的解决方案感兴趣(因为这会消耗 100% CPU);
- 睡眠(因为程序不会立即响应
System.in
)。
I have a Java thread:
class MyThread extends Thread {
@Override
public void run() {
BufferedReader stdin =
new BufferedReader(new InputStreamReader(System.in));
String msg;
try {
while ((msg = stdin.readLine()) != null) {
System.out.println("Got: " + msg);
}
System.out.println("Aborted.");
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
}
In another thread, how do I abort the stdin.readline()
call in this thread, so that this thread prints Aborted.
? I have tried System.in.close()
, but that doesn't make any difference, stdin.readline()
is still blocking.
I'm interested in solutions without
- busy waiting (because that burns 100% CPU);
- sleeping (because then the program doesn't respond instantly to
System.in
).
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
Heinz Kabutz 的新闻通讯展示了如何中止
System.in
读取:现在,我不知道这种方法是否存在泄漏、不可移植或有任何不明显的副作用。就我个人而言,我不愿意使用它。
您也许可以使用 NIO 通道 和 文件描述符 - 我自己对它们的实验没有产生任何结果。
Heinz Kabutz's newsletter shows how to abort
System.in
reads:Now, I don't know whether this approach leaks, isn't portable or has any non-obvious side-effects. Personally, I would be reluctant to use it.
You might be able to do something with NIO channels and file descriptors - my own experiments with them didn't yield any results.
怎么样...
调用
stdInch.readline()
的线程现在是可中断的,并且 readline() 将抛出java.nio.channels.ClosedByInterruptException
。How about...
A thread where
stdInch.readline()
is called is now interruptible and the readline() will throw ajava.nio.channels.ClosedByInterruptException
.InputStream
永远不会抛出InterruptedException
,因为它不在其契约中。 (在阅读之前,您应该不断检查available
。)事实上,该异常不在大多数类和方法的约定中!(而且在大多数情况下,它们不必费心调用available
。)一个更大的问题是
InterruptibleChannel
(合约中确实有它)也不能保证成功。有些通道谎称是可中断的,而使用通道的类无论如何都可能会阻塞。例如,您可能认为Channels.newChannel(System.in)
会给您一个不错的InterruptibleChannel
。但这是一个谎言,源代码注释“并非真正可中断”和“最多阻止一次”(OpenJDK 16)证明了这一点。 (是的,它确实会阻塞,我已经检查过。荒谬!)我发现有效的组合是将
new FileInputStream(FileDescriptor.in).getChannel()
与一起使用扫描仪
。这确实不应该是一个问题。编写一个检查
System.in.available()
、Thread.interrupted()
、抛出InterruptedException
等的类很简单。InputStream
can never throwInterruptedException
because it's not in its contract. (You're supposed to keep checkingavailable
before reading.) In fact, that exception is not in the contract of most classes and methods! (And for the most part they don't bother callingavailable
.)A bigger problem is that
InterruptibleChannel
(which does have it in the contract) doesn't guarantee success either. Some Channels lie about being interruptible, while classes that use the channels may block anyway. For example, you might thinkChannels.newChannel(System.in)
will get you a niceInterruptibleChannel
. But it's a lie that is attested to by the source code comments "Not really interruptible" and "Block at most once" (OpenJDK 16). (Yes, it really does block, I've checked. Ridiculous!)A combination that I've found to work is to use
new FileInputStream(FileDescriptor.in).getChannel()
withScanner
.This really shouldn't be a problem. It's straightforward to write a class that checks
System.in.available()
,Thread.interrupted()
, throwsInterruptedException
, etc. ???? Even Scanner doesn't checkavailable
if given anInputStream
, or aChannel
in blocking mode (as of OpenJDK 16). Comment if you know of a sane class that does.我的第一反应是线程和
System.in
确实不能在一起。因此,首先,将其拆分,以便线程代码不会触及任何静态,包括 System.in。
线程从
InputStream
读取数据并传入缓冲区。将InputStream
传递到现有线程中,该线程从缓冲区读取数据,同时检查您是否尚未中止。My first reaction is that a thread and
System.in
really don't go together.So first, split this so that the thread code does not touch any static including
System.in
.A thread reads from
InputStream
and passes into a buffer. Pass anInputStream
into your existing thread that reads from the buffer but also checks that you haven't aborted.BufferedReader.readLine 的 JavaDoc :
此,我认为它永远不会返回 null(System.in 实际上可以关闭吗?我认为它不会返回流结束?),因此 while 循环不会终止。停止线程的常用方法是在循环条件中使用布尔变量并从线程外部更改它,或者调用线程对象的interrupt()方法(仅当线程处于wait():ing或sleep():ing,或者在抛出 InterruptedException 的阻塞方法中)。您还可以使用 isInterrupted()。
编辑:这是一个利用
isInterrupted()
和interrupt()
的简单实现。主线程在中断工作线程之前等待 5 秒。在这种情况下,工作线程基本上处于忙等待状态,所以它不是那么好(一直循环并检查 stdin.ready() ,您当然可以让工作线程休眠一段时间如果没有输入准备好):如果 BufferedReader 在 readline 上被阻塞,或者至少我找不到一个(使用 System.in),那么似乎没有办法真正中断 BufferedReader。
JavaDoc for BufferedReader.readLine:
Based on this, I don't think it'll ever return null (can System.in actually be closed, I don't think it ever returns end of stream?), so the while-loop won't terminate. The usual way to stop a thread is either use a boolean variable in a loop condition and change it from outside of the thread or call the Thread-objects' interrupt() -method (only works if the thread is wait():ing or sleep():ing, or in a blocking method that throws InterruptedException). You can also check if the thread has been interrupted with isInterrupted().
Edit: Here's a simple implementation utilizing
isInterrupted()
andinterrupt()
. The main-thread waits 5 seconds before interrupting the worker-thread. In this case worker-thread is basically busy-waiting, so it's not that good (looping all the time and checkingstdin.ready()
, you could of course let the worker-thread sleep for a while if no input is ready):It seems there's no way to actually interrupt the BufferedReader if it's blocked on readline, or at least I couldn't find one (using System.in).
在上面的线程类定义中定义一个字段怎么样:
Whats about defining a field within your above thread class definition like: