java 中的扫描器不是线程安全的吗?

Is the scanner in java not thread safe?

我有兴趣使用 java.util.Scanner。我正在阅读 docs 并看到一行说扫描仪在没有外部同步的情况下对于多线程使用是不安全的。我可以确认这意味着两个单独的线程中的两个单独的 Scanner 对象对两个单独的文件进行操作可能会相互干扰吗?

谁能帮我从外部同步扫描器对象以用于安全线程操作?

多线程使用是指两个线程对同一个对象进行操作。两个线程操作两个不同的对象是可以的。

如果您在两个线程中使用相同的 Scanner 实例,除非您同步对 object 的访问,否则您将遇到麻烦。但是两个独立的 Scanner 实例永远不会相互干扰。

编辑以回应询问如何同步的评论

首先,您真的确定需要同步吗?您可以在不同的线程中安全地使用不同的扫描器实例,而不会出现任何危险。一个线程可以有
Scanner s1 = new Scanner (new File ("/tmp/file1.txt");
另一个线程可以有
Scanner s2 - new Scanner (new File ("/tmp/file2.txt"));
并且没有风险。不同的扫描仪可以使用相同的文件、不同的文件或完全不同的数据源。不过,您仍然需要谨慎。正如 Stephen C below 所指出的那样,如果两个单独的 Scanner 实例使用相同的流或 reader 作为它们的输入,您仍然会遇到错误的操作,然后它们会互相窃取字符。此警告适用于使用 InputStream、Readable 和 ReadableByteChannel 的构造函数。

在多线程中使用单个扫描器的问题在于它会按顺序使用来自单个源的字符。如果您有多个线程以不同步的方式使用这些字符,那么每个线程都会获取一些字符,而 none 个线程将获取所有字符。举例说明:假设您有一个扫描器读取字符串 "qwertyuiop",并且两个单独的线程各自同时调用函数 next(),那么一个线程可能会看到 "ertip" 而另一个线程会得到"qwyuo";这将是无用的。

我的同步建议是:

  1. 不要multi-thread!甚至 body 部分的截肢也比试图使 multi-threaded 应用程序稳定、可扩展和灵活更可取!
  2. 有时候可以subclass a non-thread-safe class(或者封装委托)同步调用基class(或者委托):synchronised (this) { super.next (); }.但是不要用 Scanner 尝试这个! consumer 方法那么多,你不知道class内部是怎么实现的,注定要失败!见建议 1。
  3. 我在这里尝试做的是让扫描器有一个线程 运行 并将令牌送入 ArrayBlockingQueue。这样,您将获得以正确顺序进入 queue 的完整令牌。您可以从 queue 阅读任意多的线程。但请注意,您的任何线程都可能在读取或写入此 queue 时被阻塞,除非您注意处理满和空的情况。很可能无论您做什么,您最终都会遇到永远无法完成的悬空线程。请参阅第 1 点。如果您有时想调用不同的 next 方法(例如 nextInt()nextDouble())或使用 has,这将变得复杂 方法(例如 hasNextInt()hasNextDouble()),但没有第 2 点那么复杂。

最后,我建议你看第1点

您可以毫无问题地使用来自多个线程的不同数据源的多个扫描器。

一个扫描器多个线程一起使用会导致问题。

我想在接受的答案中强调这一点。

If you use the same instance of Scanner in two threads you will have trouble unless you synchronise access to the object. But two separate instances of Scanner will never interfere with each other.

实际上,如果两个单独的 Scanner 实例 可以 相互干扰,如果它们共享相同的输入源。考虑是否有两个 Scanner 对象包装 System.in 由两个不同的线程使用。当您在 Scanner 上调用 hasNextInt() 时,它会提前读取 System.in 足够的字符以确定是否存在有效整数。如果第一个线程没有调用 nextInt(),第二个线程将无法读取预读字符。他们将坐在第一个 Scanner.

的内部缓冲区中

实际上,一个扫描仪干扰了另一个 "stealing" 个字符。在同一个流上有两个扫描仪是病态的。您的陈述仅适用于不同输入流上的两个扫描器。