16

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).
pts
  • 80,836
  • 20
  • 110
  • 183
  • Interesting. It would also be interesting to know how to abort a thread listening to incoming client-socket data. Hopefully the solution is pretty similar to this problem :) – rzetterberg May 15 '11 at 11:46
  • This is a more general question that reading from stdin - The java.io stuff is blocking I/O, and there are many existing questions about this more general problem. – skaffman May 15 '11 at 11:50
  • @Ancide: For sockets, I have a solution: closing the socket in another thread makes the `readLine` in `MyThread` raise a `SocketException` with `"Socket closed"`. So I can abort `MyThread` this way. – pts May 15 '11 at 13:20

6 Answers6

9

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.

eis
  • 51,991
  • 13
  • 150
  • 199
McDowell
  • 107,573
  • 31
  • 204
  • 267
  • Thank you for these suggestions. I also think it could work with NIO somehow, but it would be too much work to rewrite my program to use NIO, so I'm looking for other options now. `ExecutorService`, as you describe it, would definitely work, but I'm still looking for something less ugly. – pts May 15 '11 at 13:37
  • 2
    hi pardon me if i understand incorrectly, after reading the article, it seem to suggest not so much about ExecutorService (which is used to grab the result from the thread), but more about using BufferedStream ready() and sleeping so that the thread can be interruptible. – goh Oct 24 '12 at 07:48
  • @goh - Looking at my code again, I believe you are correct. I'll remove the sample & leave the link. – McDowell Oct 24 '12 at 12:54
  • Indeed it's just using `Thread.sleep(200)`... not sure why this is the top-voted answer. – Patrick Parker Mar 28 '18 at 15:57
  • @PatrickParker what's wrong with using thread.sleep here? it achieves the goal that was wanted, letting the thread process any interruptions. That's the way it should be done IMO. – eis Nov 07 '21 at 10:19
  • It probably does have the problem of not aborting if readLine() blocks, but other than that it should work, which was what was asked here. – eis Nov 07 '21 at 10:27
  • @eis - what was asked = "I'm interested in solutions without sleeping" – Patrick Parker Nov 07 '21 at 10:44
  • @PatrickParker true, I did not notice that. However, 200ms of sleeping shouldn't be much of an issue in any normal scenario. – eis Nov 07 '21 at 17:35
  • @PatrickParker based on edit history, that was added after adding this answer – eis Nov 07 '21 at 17:36
  • @eis not sure how relevant it is to discuss edit history here; it is only the current Answer which we should be commenting on. However if you go back to the original version of the code without polling then you are back to the original problem of "stdin.readline() is still blocking." If still in doubt, please gather all your thoughts and post a new question which links back to this question rather than using the comment section for that. – Patrick Parker Nov 07 '21 at 17:58
  • @PatrickParker um, I was offering an explanation on your comment "not sure why this is the top-voted answer.". I certainly don't feel like posting a new question about your comments? – eis Nov 07 '21 at 18:08
  • @eis - ok I thought you were talking about "what's wrong" and "the way it should be done." but yes, that is one possible explanation of why people might have up-voted this answer despite that it failed to meet an explicit criterion. Thanks. The topic of why people want to avoid polling every 200ms could be discussed at length elsewhere, possibly another question, if you want to debate that. I will not go into that here. – Patrick Parker Nov 07 '21 at 18:41
4

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.

pts
  • 80,836
  • 20
  • 110
  • 183
2

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.

Aleksandr Dubinsky
  • 22,436
  • 15
  • 82
  • 99
1

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.

Tom Hawtin - tackline
  • 145,806
  • 30
  • 211
  • 305
  • Thank you for suggesting not to use statics in threads, it's a nice coding style improvement. But it's also irreleveant to my question. – pts May 15 '11 at 13:23
  • 3
    ``also checks that you haven't aborted'' -- yes, exactly that's I want to do but I don't know how to do this when the thread is blocked `stdin.readLine()`. Could you please elaborate? – pts May 15 '11 at 13:23
  • 1
    @pts Statics are relevant to your question. / Two threads: One reads from the stream and blocks on I/O; one (your existing thread) reads from a buffer and blocks on a lock. – Tom Hawtin - tackline May 15 '11 at 13:40
  • It would be nice to see some example code that implements this idea. Your description is a bit vague. I answered a [similar question](https://stackoverflow.com/a/49530380/7098259) and provided some simple code that uses PipedInputStream as an example. – Patrick Parker Mar 28 '18 at 18:14
0

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

esaj
  • 15,875
  • 5
  • 38
  • 52
  • 1
    Thank you for composing your answer, but it doesn't solve my problem. Calling `myThread.interrupt()` does not interrupt the `readLine()` call on `System.in`, not even after `System.in.close()`. – pts May 15 '11 at 13:17
  • Doesn't work on Windows. Windows does not echo input during ready(), so not suitable for an interactive solution. –  Mar 12 '12 at 12:23
-2

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...
    }
  }
}
Omnaest
  • 3,096
  • 1
  • 19
  • 18
  • does calling System.in.ready() before actually reading out of the stream helps? – Omnaest May 15 '11 at 12:19
  • @user625146: Thank you for coming up with your proposal, but it doesn't work. I fail to see how setting `abortThread` would abort the call to `System.in.readline()`. – pts May 15 '11 at 13:28
  • @user625146: What if ready() returns false? How do I make `MyThread` wait for the next line, without busy waiting (100% CPU use)? – pts May 15 '11 at 13:32
  • Putting your thread into sleep with a predefined amount of time. This can be done by a second surrounding while loop. You enter your read-while-loop only if the reader is ready if not you put your thread to sleep. Does this work? – Omnaest May 15 '11 at 13:43