如果异步线程在等待 nextLine() 时被中断并且另一个 nextLine() 被调用,Java Scanner 会抛出 IndexOutOfBoundsException

问题描述 投票:0回答:1

标题说的是什么。

如果异步线程在等待

Scanner

 时被中断并且另一个 
IndexOutOfBoundsException
 被调用,
Java
nextLine()
会抛出
nextLine()

这是一些重现问题的代码。

import java.util.NoSuchElementException;
import java.util.Scanner;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.*;

/**
 * Test class for asynchronous input
 */
public class MainThreaded
{
    public static final int SLEEP_MILLIS = 200;
    public static final Object LOCK = new Object();
    public static boolean breakIfTrueAsynchronous = false;

    public static void main (String[] args) {
        BlockingQueue<String> inputQueue = new LinkedBlockingDeque<>();
        Scanner scanner = new Scanner(System.in);
        ExecutorService executor = Executors.newSingleThreadExecutor();
        Runnable inputReader = () -> {
            while(true) {
                String str;
                synchronized (scanner) {
                    str = scanner.nextLine();
                }
                System.out.println("READ THREAD");
                inputQueue.add(str);
            }
        };
        Future<?> task = executor.submit(inputReader);

        //run a timer to get the asynchronous error every 3 sec
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                synchronized (LOCK) {
                    breakIfTrueAsynchronous = true;
                }
            }
        }, 3000);

        System.out.println("Test asynchronous input started, write anything below:");
        String input = "";
        while(true) { // use ctrl+C to exit
            try {
                input = inputQueue.poll(SLEEP_MILLIS, TimeUnit.MILLISECONDS);
                if(input == null) input = ""; // if timeout elapses, avoid null value
                synchronized (LOCK){
                    if(breakIfTrueAsynchronous){
                        System.err.println("BREAK RECEIVED");
                        timer.cancel();
                        task.cancel(true);
                        executor.shutdown();
                        break; // goes to end of main below
                    }
                }
            } catch (InterruptedException e) {
                System.err.println("INTERRUPT!");
            }
            if(!input.isEmpty()) {
                //use input here
                System.out.println("INPUT RECEIVED: " + input);
                input = "";
            }
        }

        //this is only executed AFTER breakIfTrueAsynchronous IS TRUE
        System.out.println("Task cancelled: " + task.isCancelled());
        try{
            String str;
            synchronized (scanner) {
                str = scanner.nextLine();
            }
            System.out.println("INPUT RECEIVED CORRECTLY: " + str);
        }catch (IndexOutOfBoundsException e) {
            System.out.println("ERROR. IndexOutOfBoundsException thrown:");
            e.printStackTrace();
        }
    }
}

我希望最后的

scanner.nextLine();
不会抛出任何东西并继续正常阅读
System.in

相反,我得到了以下 stackTrace:

java.lang.IndexOutOfBoundsException: end    at
java.base/java.util.regex.Matcher.region(Matcher.java:1553)     at
java.base/java.util.Scanner.findPatternInBuffer(Scanner.java:1097)  at
java.base/java.util.Scanner.findWithinHorizon(Scanner.java:1800)    at
java.base/java.util.Scanner.nextLine(Scanner.java:1658)     at 
MainThreaded.main(MainThreaded.java:70)

编辑:

Scanner
上同步解决了异常问题(感谢@user207421),但现在我不明白为什么第一个任务没有被
task.cancel(true)
打断。

我现在的工作假设是问题在于线程正在等待时接收到中断

scanner.nextLine()

非常感谢任何能解决这个问题的人T.T

编辑2:

我通过仅使用线程进行输入并始终从

BlockingQueue
读取来解决此问题:D 切勿在该单个线程之外调用
scanner.nextLine()
可以防止同步错误,其余的由队列处理。这样我也永远不会停止线程,直到应用程序结束。

非常感谢@rzwitserloot 提供有关可中断方法的所有信息。

java java.util.scanner indexoutofboundsexception
1个回答
0
投票

我通过仅使用线程读取输入并始终使用

inputQueue.poll()
inputQueue.take()
来使用代码其他部分的输入解决了这个问题。

永远不要在该单线程之外调用

scanner.nextLine()
可以防止同步错误,其余的由队列处理。这样我也不会停止线程,直到应用程序结束。

需要注意的是,即使在

System.exit()
上,线程也不会停止。
在线程等待
System.exit()
时调用
scanner.nextLine()
会等待输入,然后再关闭线程和应用程序。

非常感谢@rzwitserloot 提供有关可中断方法的所有信息:

java.*
包中使用
throws InterruptedException
声明的所有方法都保证是可中断的。
readLine()
不是这些方法之一 - 这意味着它可以或不可中断。取决于操作系统、JVM 版本、供应商、芯片和月相。

感谢@Nathan 的评论:

中断通过检查标志来工作。代码被阻塞等待 I/O 无法检查标志

© www.soinside.com 2019 - 2024. All rights reserved.