22

I have a thread that executes the following code:

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);
    }
}

where in is System.in. How can I stop such thread gracefully? Neither closing System.in, nor using Thread.interrupt appear to work.

vitaut
  • 49,672
  • 25
  • 199
  • 336

7 Answers7

8

This is because reading System.in (InputStream) is a blocking operation.

Look here Is it possible to read from a InputStream with a timeout?

Community
  • 1
  • 1
PeterMmm
  • 24,152
  • 13
  • 73
  • 111
  • 1
    Thanks. Using NIO `FileChannel` extracted from `System.in` allows to stop the thread simply by closing `System.in`. Closing `System.in` results in `AsynchronousCloseException` which interrupts blocking `FileChannel.read`. – vitaut Nov 16 '10 at 07:11
8

You've stumbled upon a 9 year old bug no one is willing to fix. They say there are some workarounds in this bug report. Most probably, you'll need to find some other way to set timeout (busy waiting seems unavoidable).

Denis Tulskiy
  • 19,012
  • 6
  • 50
  • 68
  • Interestingly when I stumbled into this it was even odder behavior. The interrupt worked but took 2.5 minutes! (ubuntu 18.04) – Gus Apr 14 '20 at 16:18
1

You could use the available() method (which is non-blocking) to check whether there is anything to read beforehand.

In pseudo-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...
Paolo
  • 22,188
  • 6
  • 42
  • 49
  • 1
    Abritrary number, if you don't sleep you end up in a hot-loop spinning the CPU waiting for available bytes to read. My assumption was this thread will spend most of its time waiting for input. – Paolo Nov 15 '10 at 09:05
  • Using `available` almost does the trick. The problem is that in case of EOF `available` returns 0 and the thread continues execution. +1 to compensate for unfair -1 =) – vitaut Nov 15 '10 at 09:06
  • I just thought about responsiveness... PeterMmms link says it all: http://stackoverflow.com/questions/804951/is-it-possible-to-read-from-a-java-inputstream-with-a-timeout – dacwe Nov 15 '10 at 11:14
  • Thank you! Your answer just saved my life! – Berrigan Aug 31 '17 at 17:27
1

Is it safe to close in stream in other thread? It works for me. In this case, in.read(...) throws exception SocketException.

tdx
  • 21
  • 1
0

If you want to give a user some time to enter data - maybe to allow overriding default values or interrupting some automated process -, then wait first and check available input after the pause:

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*/ }
Andreas Dolk
  • 113,398
  • 19
  • 180
  • 268
0

I had the same problem today, and this is how I fixed it, using 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
            }
        }
    }
}
Nans
  • 146
  • 2
  • 4
  • Interesting, but isn't it taking a lot of CPU by executing the loop all the time while there is nothing to read? – vitaut Mar 01 '12 at 16:05
  • Actually that is why I made the thread wait for some time every iteration. But you are right, it is not as efficient as it could be. It depends now on what is software purpose. – Nans Mar 02 '12 at 07:45
  • 1
    Pretty sure this still hangs if there's no newline on the stream. in.ready() returns true if there's a single byte, in.readLine() reads it, then keeps waiting (forever) for the newline. – pendor Feb 19 '14 at 15:18
-3

you can use a external flag for this

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); 
    } 
} 
asela38
  • 4,546
  • 6
  • 25
  • 31