Страницы

Поиск по вопросам

среда, 22 января 2020 г.

Непонятное поведение потоков

#java #многопоточность


Пытаюсь сделать такую вещь: В консоль выводится вопрос. Если в течении некоторого
времени пользователь не вводит ответ, то задаётся следующий вопрос. Если вводит ответ,
то следующий вопрос задаётся сразу.
Мой код:

public class Test {
private boolean stopQuestion;
Thread          scannerThread = new Thread();

public static void main(final String[] args) {
    final Test test = new Test();

    test.scannerThread = new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                String string;
                do {
                    string = test.requestInput(new Thread(new Runnable() {

                        @Override
                        public void run() {
                            try {
                                Thread.sleep(3000);
                            } catch (final InterruptedException e) {
                            }
                            test.scannerThread.interrupt();

                        }
                    }));
                } while (!test.stopQuestion);

                System.out.println("Input: " + string);
            } catch (final IOException e) {
                throw new RuntimeException(e);
            }
        }
    });
    test.scannerThread.start();

}

public String requestInput(final Thread timer) throws IOException {
    final BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

    timer.start();

    System.out.println("Any question");
    System.out.println("Please type your answer: ");
    try {
        while (!br.ready()) {
            Thread.sleep(100);
        }
    } catch (final InterruptedException e) {
        System.out.println("Time is over. Next question: ");
        return null;
    }
    System.out.println("Thank You for providing input!");
    return br.readLine();
}

}


Если ничего не писать в консоль, то всё вроде работает как положено. Время выходит
и задаётся следующий вопрос. Но стоит что-то ввести, как таймер сбивается и следующий
вопрос уже не ожидает заданного количества времени, иногда он вообще не ждет. Не понимаю
в чем дело.

Пробовал вынести реализацию которая передаётся в requestInput в отдельный экземпляр
класса и передавать ссылку на этот экземпляр, но тогда выскакивает IllegalThreadStateException.
    


Ответы

Ответ 1



Может вам сделать всего два потока: производитель (ждет пользовательское сообщение и передает его куда надо) и потребитель (обработка сообщения). Эти потоки создаются сразу и синхронизируются, например, через BlockingQueue.

Ответ 2



Сразу кину работающий код, ниже объясню что не так было. public class Test { private boolean stopQuestion; Thread scannerThread = new Thread(); public static void main(final String[] args) { final Test test = new Test(); test.scannerThread = new Thread(new Runnable() { @Override public void run() { try { String string; do { string = test.requestInput(new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(3000); test.scannerThread.interrupt(); } catch (final InterruptedException e) { } } })); } while (!test.stopQuestion); System.out.println("Input: " + string); } catch (final IOException e) { throw new RuntimeException(e); } } }); test.scannerThread.start(); } public String requestInput(final Thread timer) throws IOException { final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); timer.start(); System.out.println("Any question"); System.out.println("Please type your answer: "); try { while (!br.ready()) { Thread.sleep(100); } } catch (final InterruptedException e) { System.out.println("Time is over. Next question: "); return null; } timer.interrupt(); System.out.println("Thank You for providing input!"); return br.readLine(); } } Смотри, тут изменения ровно в двух строчках. Нужно понимать, что когда ты делаешь interrupt(), то ты лишь советуешь потоку остановиться, по-сути поднимаешь флажок. Тем самым, получается следующая ситуация: Создается первый таймер (поток, который засыпает на 3 секунды и делает интерапт) Таймер засыпает, а главный трэд идет дальше. Допустим кто-то ввел текст, тогда главный трэд пишет в консольку Thank You for providing input! и выходит обратно в цикл, и создает новый таймер. Теперь первый таймер, прождав 3 секунды делает interrupt() и когда главный поток теперь сделает sleep(), то сразу же вылетит InterruptedException и он пойдет создавать новый поток и так далее... Изменения коснулись метода run() у таймера и метода requestInput(). Для понимания ситуации: test.scannerThread.interrupt(); //после этого ниче не произойдет, поток не прервется Thread.sleep(3000); //сразу же вылетает InterruptedException и флажок interrupt сбрасывается

Ответ 3



У Вас есть основной тред и треды, которые генерятся каждые 3 сек (назовем их треды-прерыватели). Если Вы вводите текст, то основной тред раньше выходит из метода requestInput. Но тред-прерыватель все еще жив и готовится прервать основной тред. Основной тред создает новый тред-прерыватель и запускает его. У нас получаются два одновременно работающих треда-прерывателя. Тред-прерыватель, созданный первым, прерывает основной поток - причем конечно раньше, чем за 3 сек. И тем самым заставляет основной поток создать еще один тред-прерыватель. Т.е. получается, что одновременно будут существовать 2 треда-прерывателя. Если Вы будете продолжать вводить текст, то можно наплодить еще тредов-прерывателей.

Комментариев нет:

Отправить комментарий