Interrupt a thread waiting for user input and then

2019-04-12 06:31发布

I have two threads running, userInputThread waits for user input from the command line and interrupterThread tries to interrupt userInputThread 1 sec after starting. Obviously you cannot interrupt a thread that is blocked by the System.in. Another answer suggests to close System.in with System.in.close() before interrupting a thread. But when I run the following code, the userInputThread never gets interrupted and the app just hangs without closing.

class InputInterruptionExample {

    private Thread userInputThread;
    private Thread interrupterThread;

    InputInterruptionExample() {
        this.userInputThread = new Thread(new UserInputThread());
        this.interrupterThread = new Thread(new InterrupterThread());
    }

    void startThreads() {
        this.userInputThread.start();
        this.interrupterThread.start();
    }
    private class UserInputThread implements Runnable {
        public void run() {
            try {
                System.out.println("enter your name: ");
                String userInput = (new BufferedReader(new InputStreamReader(System.in))).readLine();
            } catch (IOException e) {
                System.out.println("Oops..somethign went wrong.");
                System.exit(1);
            }
        }
    }
    private class InterrupterThread implements Runnable {
        public void run() {
            try {
                sleep(1000);
                System.out.println("about to interrupt UserInputThread");
                System.in.close();
                userInputThread.interrupt();
                userInputThread.join();
                System.out.println("Successfully interrupted");
            } catch (InterruptedException e) {
            } catch (IOException ex) {
                System.out.println("Oops..somethign went wrong.");
                System.exit(1);
            }
        }
    }
    public static void main(String[] args) {
        InputInterruptionExample exampleApp = new InputInterruptionExample();
        exampleApp.startThreads();
    }
}

There's already a similar question, but there aren't any definite answers.

2条回答
虎瘦雄心在
2楼-- · 2019-04-12 07:04

All of my research leads me to believe that the underlying .read() in the .readLine() call cannot be interrupted (Without destroying the Process that System.in is attached to, at least). The only other choices at that point is to use a polling IO scheme or switch to NIO.

Here's a quick (and very dirty/ugly) adaptation of your code into a polling IO scheme. It's not an interupt solution so it's not directly answering your question, but rather hopefully getting you the behavior you desire.

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.concurrent.atomic.AtomicBoolean;

public class InputInterruptionExample {

    private UserInputThread uiThreadObj = null;
    private Thread inputThread = null;
    private Thread interrupThread = null;

    public InputInterruptionExample() {
        this.uiThreadObj = new UserInputThread();
        this.inputThread = new Thread(this.uiThreadObj);
        this.interrupThread = new Thread(new InterrupterThread());
    }

    void startThreads() {
        this.inputThread.start();
        this.interrupThread.start();
    }

    private class UserInputThread implements Runnable {
        private final AtomicBoolean runCmd = new AtomicBoolean(true);

        public void run() {
            try {
                final BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

                System.out.println("enter your name: ");
                while (this.runCmd.get()) {
                    if (System.in.available() > 0) {
                        String userInput = br.readLine();
                        System.out.println("You typed: " + userInput);
                        System.out.println("enter your name: ");
                    } else {
                        Thread.sleep(5); //minimal sleep to prevent CPU peg
                    }
                }
                System.out.println("Finishing normally.");

            } catch (IOException e) {
                System.out.println("Oops..somethign went wrong.");
                System.exit(1);
            } catch (Exception e) {
                System.out.println("What'd you do?!");
                e.printStackTrace();
            }
        }

        public final void requestStop() {
            this.runCmd.set(false);
        }
    }
    private class InterrupterThread implements Runnable {
        public void run() {
            try {
                Thread.sleep(1000 * 10);
                System.out.println("Requesting that UserInputThread stop.");
                uiThreadObj.requestStop();
                System.out.println("Request made.");
            } catch (InterruptedException e) {
                System.out.println("Oops..somethign went wrong.");
                System.exit(1);
            }
        }
    }
    public static void main(String[] args) {
        InputInterruptionExample exampleApp = new InputInterruptionExample();
        exampleApp.startThreads();
    }
}
查看更多
戒情不戒烟
3楼-- · 2019-04-12 07:09

This has solved the problem:

class InputInterruptionExample {

    private UserInputThread userInputRunnable;
    private Thread userInputThread;
    private Thread interrupterThread;

    InputInterruptionExample() {
        this.userInputRunnable = new UserInputThread();
        this.userInputThread = new Thread(userInputRunnable);
        this.interrupterThread = new Thread(new InterrupterThread());
    }

    void startThreads() {
        this.userInputThread.start();
        this.interrupterThread.start();
    }
    private class UserInputThread implements Runnable {
        private InputStreamReader isr;
        private BufferedReader br;

        UserInputThread() {
            InputStreamReader isr = new InputStreamReader(System.in);
            BufferedReader br = new BufferedReader(isr);
        }

        public void run() {
            try {
                System.out.println("enter your name: ");
                try{
                    String userInput = br.readLine();
                } catch(NullPointerException e) {}
            } catch (IOException e) {
                System.out.println("Oops..somethign went wrong.");
                System.exit(1);
            }
        }
        public void closeBufferdReader() {
            try {
                System.in.close();
            } catch (IOException e) {
                System.out.println("Oops..somethign went wrong in closeBufferdReader() method");
                System.exit(1);
            }
        }
    }
    private class InterrupterThread implements Runnable {
        public void run() {
            try {
                sleep(1000);
                userInputRunnable.closeBufferdReader();                 
                userInputThread.interrupt();
                userInputThread.join();
                System.out.println("Successfully interrupted");
            } catch (InterruptedException e) {}
        }
    }
    public static void main(String[] args) {
        InputInterruptionExample exampleApp = new InputInterruptionExample();
        exampleApp.startThreads();
    }
}

Update: This only works when BufferedReader is split up this way:

InputStreamReader isr = new InputStreamReader(System.in);
BufferedReader br = new BufferedReader(isr);
String userInput = br.readLine();

For some reason the interruption does not seem to work when the readLine() structure is written as a oneliner:

this.userInput = (new BufferedReader(new InputStreamReader(System.in))).readLine();

So while it is possible to interrupt the thread in the split-up BufferedReader structure, it is now impossible to read user's input.

If someone could show a way to be able to get user input as well as interrupt the UserInputThread when the user doesn't provide any input in time (while interrupter is sleeping), please do.

查看更多
登录 后发表回答