Java:如何中止从 System.in 读取的线程

发布于 2024-11-07 07:59:09 字数 706 浏览 1 评论 0原文

我有一个 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

不知所踪 2024-11-14 07:59:09

Heinz Kabutz 的新闻通讯展示了如何中止 System.in 读取:

import java.io.*;
import java.util.concurrent.*;

class ConsoleInputReadTask implements Callable<String> {
  public String call() throws IOException {
    BufferedReader br = new BufferedReader(
        new InputStreamReader(System.in));
    System.out.println("ConsoleInputReadTask run() called.");
    String input;
    do {
      System.out.println("Please type something: ");
      try {
        // wait until we have data to complete a readLine()
        while (!br.ready()) {
          Thread.sleep(200);
        }
        input = br.readLine();
      } catch (InterruptedException e) {
        System.out.println("ConsoleInputReadTask() cancelled");
        return null;
      }
    } while ("".equals(input));
    System.out.println("Thank You for providing input!");
    return input;
  }
}

public class ConsoleInput {
  private final int tries;
  private final int timeout;
  private final TimeUnit unit;

  public ConsoleInput(int tries, int timeout, TimeUnit unit) {
    this.tries = tries;
    this.timeout = timeout;
    this.unit = unit;
  }

  public String readLine() throws InterruptedException {
    ExecutorService ex = Executors.newSingleThreadExecutor();
    String input = null;
    try {
      // start working
      for (int i = 0; i < tries; i++) {
        System.out.println(String.valueOf(i + 1) + ". loop");
        Future<String> result = ex.submit(
            new ConsoleInputReadTask());
        try {
          input = result.get(timeout, unit);
          break;
        } catch (ExecutionException e) {
          e.getCause().printStackTrace();
        } catch (TimeoutException e) {
          System.out.println("Cancelling reading task");
          result.cancel(true);
          System.out.println("\nThread cancelled. input is null");
        }
      }
    } finally {
      ex.shutdownNow();
    }
    return input;
  }
}

现在,我不知道这种方法是否存在泄漏、不可移植或有任何不明显的副作用。就我个人而言,我不愿意使用它。

您也许可以使用 NIO 通道文件描述符 - 我自己对它们的实验没有产生任何结果。

Heinz Kabutz's newsletter shows how to abort System.in reads:

import java.io.*;
import java.util.concurrent.*;

class ConsoleInputReadTask implements Callable<String> {
  public String call() throws IOException {
    BufferedReader br = new BufferedReader(
        new InputStreamReader(System.in));
    System.out.println("ConsoleInputReadTask run() called.");
    String input;
    do {
      System.out.println("Please type something: ");
      try {
        // wait until we have data to complete a readLine()
        while (!br.ready()) {
          Thread.sleep(200);
        }
        input = br.readLine();
      } catch (InterruptedException e) {
        System.out.println("ConsoleInputReadTask() cancelled");
        return null;
      }
    } while ("".equals(input));
    System.out.println("Thank You for providing input!");
    return input;
  }
}

public class ConsoleInput {
  private final int tries;
  private final int timeout;
  private final TimeUnit unit;

  public ConsoleInput(int tries, int timeout, TimeUnit unit) {
    this.tries = tries;
    this.timeout = timeout;
    this.unit = unit;
  }

  public String readLine() throws InterruptedException {
    ExecutorService ex = Executors.newSingleThreadExecutor();
    String input = null;
    try {
      // start working
      for (int i = 0; i < tries; i++) {
        System.out.println(String.valueOf(i + 1) + ". loop");
        Future<String> result = ex.submit(
            new ConsoleInputReadTask());
        try {
          input = result.get(timeout, unit);
          break;
        } catch (ExecutionException e) {
          e.getCause().printStackTrace();
        } catch (TimeoutException e) {
          System.out.println("Cancelling reading task");
          result.cancel(true);
          System.out.println("\nThread cancelled. input is null");
        }
      }
    } finally {
      ex.shutdownNow();
    }
    return input;
  }
}

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.

拔了角的鹿 2024-11-14 07:59:09

怎么样...

private static BufferedReader stdInCh = new BufferedReader(
    new InputStreamReader(Channels.newInputStream((
    new FileInputStream(FileDescriptor.in)).getChannel())));

调用 stdInch.readline() 的线程现在是可中断的,并且 readline() 将抛出 java.nio.channels.ClosedByInterruptException

How about...

private static BufferedReader stdInCh = new BufferedReader(
    new InputStreamReader(Channels.newInputStream((
    new FileInputStream(FileDescriptor.in)).getChannel())));

A thread where stdInch.readline() is called is now interruptible and the readline() will throw a java.nio.channels.ClosedByInterruptException.

梦罢 2024-11-14 07:59:09

InputStream 永远不会抛出 InterruptedException,因为它不在其契约中。 (在阅读之前,您应该不断检查available。)事实上,该异常不在大多数类和方法的约定中!(而且在大多数情况下,它们不必费心调用 available。)

一个更大的问题是 InterruptibleChannel (合约中确实有它)也不能保证成功。有些通道谎称是可中断的,而使用通道的类无论如何都可能会阻塞。例如,您可能认为 Channels.newChannel(System.in) 会给您一个不错的 InterruptibleChannel。但这是一个谎言,源代码注释“并非真正可中断”和“最多阻止一次”(OpenJDK 16)证明了这一点。 (是的,它确实会阻塞,我已经检查过。荒谬!)

我发现有效的组合是将 new FileInputStream(FileDescriptor.in).getChannel()一起使用扫描仪

Scanner scanner = new Scanner(new FileInputStream(FileDescriptor.in).getChannel())

while (!scanner.hasNextLine())
    Thread.sleep(100); // Internally checks Thread.interrupted() and throws InterruptedException

String line = scanner.nextLine()

这确实不应该是一个问题。编写一个检查 System.in.available()Thread.interrupted()、抛出 InterruptedException 等的类很简单。

InputStream can never throw InterruptedException because it's not in its contract. (You're supposed to keep checking available 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 calling available.)

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 think Channels.newChannel(System.in) will get you a nice InterruptibleChannel. 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() with Scanner.

Scanner scanner = new Scanner(new FileInputStream(FileDescriptor.in).getChannel())

while (!scanner.hasNextLine())
    Thread.sleep(100); // Internally checks Thread.interrupted() and throws InterruptedException

String line = scanner.nextLine()

This really shouldn't be a problem. It's straightforward to write a class that checks System.in.available(), Thread.interrupted(), throws InterruptedException, etc. ???? Even Scanner doesn't check available if given an InputStream, or a Channel in blocking mode (as of OpenJDK 16). Comment if you know of a sane class that does.

述情 2024-11-14 07:59:09

我的第一反应是线程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 an InputStream into your existing thread that reads from the buffer but also checks that you haven't aborted.

夜空下最亮的亮点 2024-11-14 07:59:09

BufferedReader.readLine 的 JavaDoc

返回:
包含该行内容的字符串,不包括任何内容
行终止字符,或 null
如果流的末尾已经
达到

此,我认为它永远不会返回 null(System.in 实际上可以关闭吗?我认为它不会返回流结束?),因此 while 循环不会终止。停止线程的常用方法是在循环条件中使用布尔变量并从线程外部更改它,或者调用线程对象的interrupt()方法(仅当线程处于wait():ing或sleep():ing,或者在抛出 InterruptedException 的阻塞方法中)。您还可以使用 isInterrupted()

编辑:这是一个利用 isInterrupted()interrupt() 的简单实现。主线程在中断工作线程之前等待 5 秒。在这种情况下,工作线程基本上处于忙等待状态,所以它不是那么好(一直循环并检查 stdin.ready() ,您当然可以让工作线程休眠一段时间如果没有输入准备好):

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;


public class MyThreadTest
{
    public static void main(String[] args)
    {
        MyThread myThread = new MyThread();
        myThread.start();

        try
        {
            Thread.sleep(5000);
        }
        catch(InterruptedException e)
        {
            //Do nothing
        }

        myThread.interrupt();

    }

    private static class MyThread extends Thread
    {       
        @Override
        public void run()
        {
            BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in));
            String msg;

            while(!isInterrupted())
            {
                try
                {
                    if(stdin.ready())
                    {
                        msg = stdin.readLine();
                        System.out.println("Got: " + msg);
                    }
                }
                catch(IOException e)
                {
                    e.printStackTrace();
                }
            }           
            System.out.println("Aborted.");
        }
    }

}

如果 BufferedReader 在 readline 上被阻塞,或者至少我找不到一个(使用 System.in),那么似乎没有办法真正中断 BufferedReader。

JavaDoc for BufferedReader.readLine:

Returns:
A String containing the contents of the line, not including any
line-termination characters, or null
if the end of the stream has been
reached

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() and interrupt(). 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 checking stdin.ready(), you could of course let the worker-thread sleep for a while if no input is ready):

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;


public class MyThreadTest
{
    public static void main(String[] args)
    {
        MyThread myThread = new MyThread();
        myThread.start();

        try
        {
            Thread.sleep(5000);
        }
        catch(InterruptedException e)
        {
            //Do nothing
        }

        myThread.interrupt();

    }

    private static class MyThread extends Thread
    {       
        @Override
        public void run()
        {
            BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in));
            String msg;

            while(!isInterrupted())
            {
                try
                {
                    if(stdin.ready())
                    {
                        msg = stdin.readLine();
                        System.out.println("Got: " + msg);
                    }
                }
                catch(IOException e)
                {
                    e.printStackTrace();
                }
            }           
            System.out.println("Aborted.");
        }
    }

}

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).

养猫人 2024-11-14 07:59:09

在上面的线程类定义中定义一个字段怎么样:

class MyThread extends Thread {   

  protected AtomicBoolean abortThread = new AtomicBoolean(false);

  public void doAbort()
  {
    this.abortThread.set(true);
  }

  @Override   public void run() 
  { 
    ...
    if (this.abortThread.get())
    {
      ...something like break loop...
    }
  }
}

Whats about defining a field within your above thread class definition like:

class MyThread extends Thread {   

  protected AtomicBoolean abortThread = new AtomicBoolean(false);

  public void doAbort()
  {
    this.abortThread.set(true);
  }

  @Override   public void run() 
  { 
    ...
    if (this.abortThread.get())
    {
      ...something like break loop...
    }
  }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文