Java: how to abort a thread reading from System.in
Asked Answered
G

6

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).
Ganley answered 15/5, 2011 at 11:42 Comment(3)
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 :)Provoke
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.Concoction
@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.Ganley
D
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.

Description answered 15/5, 2011 at 12:0 Comment(4)
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.Ganley
``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?Ganley
@Ganley 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.Description
It would be nice to see some example code that implements this idea. Your description is a bit vague. I answered a similar question and provided some simple code that uses PipedInputStream as an example.Stewardson
B
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.

Blepharitis answered 15/5, 2011 at 13:14 Comment(12)
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.Ganley
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.Alkalimeter
@Alkalimeter - Looking at my code again, I believe you are correct. I'll remove the sample & leave the link.Blepharitis
Indeed it's just using Thread.sleep(200)... not sure why this is the top-voted answer.Stewardson
@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.Vincenty
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.Vincenty
@Vincenty - what was asked = "I'm interested in solutions without sleeping"Stewardson
@PatrickParker true, I did not notice that. However, 200ms of sleeping shouldn't be much of an issue in any normal scenario.Vincenty
@PatrickParker based on edit history, that was added after adding this answerVincenty
@Vincenty 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.Stewardson
@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?Vincenty
@Vincenty - 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.Stewardson
D
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.

Dubuffet answered 13/12, 2011 at 0:44 Comment(3)
Doesn't work for example on Mac OS X with JDK 1.7. readLine() does not react on interrupt().Propound
Same here :( I had faith in non blockingBless
This approach is dumb. None of the InputStream methods throw InterruptedException, therefore ChannelInputStream (returned by Channels.newInputStream) does its best to restore the usual blocking behavior.Thermodynamic
T
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.

Thermodynamic answered 19/10, 2021 at 19:35 Comment(0)
D
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.

Description answered 15/5, 2011 at 12:0 Comment(4)
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.Ganley
``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?Ganley
@Ganley 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.Description
It would be nice to see some example code that implements this idea. Your description is a bit vague. I answered a similar question and provided some simple code that uses PipedInputStream as an example.Stewardson
M
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).

Matsumoto answered 15/5, 2011 at 11:54 Comment(2)
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().Ganley
Doesn't work on Windows. Windows does not echo input during ready(), so not suitable for an interactive solution.Propound
S
-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...
    }
  }
}
Sassanid answered 15/5, 2011 at 11:55 Comment(4)
does calling System.in.ready() before actually reading out of the stream helps?Sassanid
@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().Ganley
@user625146: What if ready() returns false? How do I make MyThread wait for the next line, without busy waiting (100% CPU use)?Ganley
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?Sassanid

© 2022 - 2024 — McMap. All rights reserved.