如何停止在 Java 中的阻塞读取操作中等待的线程?

Posted

技术标签:

【中文标题】如何停止在 Java 中的阻塞读取操作中等待的线程?【英文标题】:How to stop a thread waiting in a blocking read operation in Java? 【发布时间】:2011-05-10 02:38:32 【问题描述】:

我有一个执行以下代码的线程:

public void run() 
    try 
        int n = 0;
        byte[] buffer = new byte[4096];
        while ((n = in.read(buffer)) != -1) 
            out.write(buffer, 0, n);
            out.flush();
        
     catch (IOException e) 
        System.out.println(e);
    

其中inSystem.in。我怎样才能优雅地停止这样的线程?关闭 System.in 或使用 Thread.interrupt 似乎都不起作用。

【问题讨论】:

使用中断会发生什么? @tulskiy:线程继续运行,直到手动输入 Ctrl-D (EOF)。 【参考方案1】:

这是因为读取 System.in (InputStream) 是一个阻塞操作。

看这里Is it possible to read from a InputStream with a timeout?

【讨论】:

谢谢。使用从System.in 中提取的NIO FileChannel 可以通过关闭System.in 来停止线程。关闭System.in 会导致AsynchronousCloseException 中断阻塞FileChannel.read【参考方案2】:

您偶然发现了一个 9 岁的 bug 没有人愿意修复。他们说this bug report 中有一些解决方法。很可能,您需要找到其他方法来设置超时(忙于等待似乎是不可避免的)。

【讨论】:

有趣的是,当我偶然发现这一点时,它的行为更加奇怪。中断有效,但花了 2.5 分钟! (ubuntu 18.04)【参考方案3】:

你可以使用 available() 方法(它是非阻塞的)来检查是否有预先读取的内容。

在伪java中:

//...
while(running)

    if(in.available() > 0)
    
        n = in.read(buffer);
        //do stuff with the buffer
    
    else
    
        Thread.sleep(500);
    

//when running set to false exit gracefully here...

【讨论】:

Abritraary number,如果你不睡觉,你最终会进入一个热循环,让 CPU 等待读取可用字节。我的假设是这个线程将花费大部分时间等待输入。 使用available 几乎可以解决问题。问题是如果 EOF available 返回 0 并且线程继续执行。 +1 补偿不公平 -1 =) 我只是想到了响应能力...... PeterMmms 链接说明了一切:***.com/questions/804951/… 谢谢!你的回答刚刚救了我的命!【参考方案4】:

在其他线程中关闭流是否安全? 这个对我有用。在这种情况下,in.read(...) 会抛出异常 SocketException

【讨论】:

【参考方案5】:

如果您想给用户一些时间来输入数据 - 可能允许覆盖默认值或中断一些自动化过程 - 然后先等待并在暂停后检查可用输入:

System.out.println("Enter value+ENTER within 5 Seconds to override default value: ");
try
  Thread.sleep(5000);
 catch InterruptedException e)

try
  int bytes = System.in.available();
  if (bytes > 0) 
    System.out.println("Using user entered data ("+size+" bytes)");
   else 
    System.out.println("Using default value"); 
  
 catch(IOException e)  /*handle*/ 

【讨论】:

【参考方案6】:

我今天遇到了同样的问题,我就是这样解决的,使用in.ready()

public void run() 
    String line;
    // Some code

    while(!Thread.currentThread().isInterrupted())
        try 
            if (in.ready()) 
                line = in.readLine();
             
         catch (Exception e) 
            try 
                Thread.currentThread().wait(500);
             catch (InterruptedException e1) 
                // Do what we want when thread is interrupted
            
        
    

【讨论】:

很有趣,但是在没有什么可读取的情况下一直执行循环不是要占用大量 CPU 吗? 实际上这就是为什么我让线程每次迭代都等待一段时间。但你是对的,它没有它应该的那么有效。现在取决于软件的用途。 很确定如果流中没有换行符,这仍然会挂起。如果有单个字节, in.ready() 返回 true,in.readLine() 读取它,然后继续(永远)等待换行符。【参考方案7】:

您可以为此使用外部标志

boolean flag = true;


public void run()  
    try  
        int n = 0; 
        byte[] buffer = new byte[4096]; 
        while ((n = in.read(buffer)) != -1 && flag)  
            out.write(buffer, 0, n); 
            out.flush(); 
         
     catch (IOException e)  
        System.out.println(e); 
     
 

【讨论】:

这不会中断已经阻塞的调用,所以它不能解决问题。

以上是关于如何停止在 Java 中的阻塞读取操作中等待的线程?的主要内容,如果未能解决你的问题,请参考以下文章

Springboot Java多线程操作本地文件,加读写锁,阻塞的线程等待运行中的线程执行完再查询并写入

在java中停止阻塞套接字

如何立即终止套接字 IO 操作上的线程阻塞?

Java基础之线程阻塞队列

在java多线程程序中,怎样实时找出处于等待(阻塞)状态线程、进程的个数。

聊聊并发——Java中的阻塞队列