7

我想设置一个用 Java 读取的阻塞文件。也就是说,有一个文件,当被调用FileInputStream任何read()方法时,调用会阻塞。

我想不出一种简单的独立于操作系统的方式——在类 Unix 操作系统上,我可以尝试使用mkfifo该文件创建一个 FIFO 并从中读取。一种可能的解决方法是创建一个非常大的文件并从中读取 - 在我捕获堆栈之前读取不太可能完成,但它丑陋且缓慢(实际上,缓存时读取仍然可以非常快)。

相应的套接字read()情况很容易设置 - 自己创建一个套接字并从中读取,您可以进行确定性阻塞。

目的是检查方法的堆栈以确定在这种情况下的顶部帧是什么。想象一下,我有一个组件,它定期对所有正在运行的线程的堆栈跟踪进行采样,然后尝试对该线程目前正在做什么进行分类。它可以做的一件事是文件 IO。所以我需要知道文件 IO 期间的“栈顶”是什么样的。我已经通过实验确定了这一点(只需以各种方式读取文件并对堆栈进行采样),但我想编写一个测试,如果这种情况发生变化,它将失败。

编写此类测试的自然方法是启动一个执行文件读取的线程,然后检查顶部帧。为了可靠地做到这一点,我想要一个阻塞读取(否则线程可能会在获取堆栈跟踪之前完成它的读取,等等)。

4

6 回答 6

6

要获得有保证的阻塞 I/O,请从控制台读取,例如/dev/console在 Linux 或CONWindows 上。

为了使这个平台独立,你可以破解FileDescriptorFileInputStream

    // Open a dummy FileInputStream
    File f = File.createTempFile("dummy", ".tmp");
    f.deleteOnExit();
    FileInputStream fis = new FileInputStream(f);

    // Replace FileInputStream's descriptor with stdin
    Field fd = FileInputStream.class.getDeclaredField("fd");
    fd.setAccessible(true);
    fd.set(fis, FileDescriptor.in);

    System.out.println("Reading...");
    fis.read();
    System.out.println("Complete");

更新

我意识到你甚至不需要阻止的方法。为了获得正确的堆栈跟踪,您可以read()在无效的 FileInputStream 上调用:

    FileInputStream fis = new FileInputStream(new FileDescriptor());
    fis.read(); // This will throw IOException exactly with the right stacktrace

如果您仍然需要阻塞read(),命名管道是要走的路:在 POSIX 系统上运行mkfifo使用或在 Windows 上创建。Runtime.exec\\.\PIPE\MyPipeName

于 2015-01-29T00:44:48.663 回答
1

无论如何,我不知道以独立于操作系统的方式制作文件,该文件在读取时总是会阻塞。

如果我试图在调用特定函数时查找堆栈跟踪,我会在调试器下运行程序并在该函数上设置断点。虽然,如果时间很重要,方法断点会减慢您的程序并给您带来不同的结果。

如果您可以访问该程序的源代码,您可以制作一个假的 FileInputStream 来扩展真实的,但总是在读取时阻塞。您需要做的就是在整个代码中切换导入语句。但是,这不会捕获您无法切换导入语句的地方,如果有很多代码,这可能会很痛苦。

如果您想在不更改程序源代码或编译的情况下使用自己的 FileInputStream,您可以制作一个自定义类加载器来加载您的自定义 FileInputStream 类而不是真实的类。您可以通过以下方式指定要在命令行上使用的类加载器:

java -Djava.system.class.loader=com.test.MyClassLoader xxx

现在我想了想,我有了一个更好的主意,而不是制作一个在 read() 上阻塞的自定义 FileInputStream,而是制作一个在 read() 上打印出堆栈跟踪的自定义 FileInputStream。然后自定义类可以调用真实版本的 read()。这样,您将获得所有调用的所有堆栈跟踪。

于 2015-01-20T23:48:50.573 回答
1

据我了解,您想编写一个检查FileInputStream.read()方法堆栈跟踪的测试。FileInputStream如果他们覆盖该read()方法的后代呢?

如果您不需要检查后代,我认为您可以通过在运行时在所需方法中插入断点来使用JVM 工具接口,并在此事件的事件处理中(断点) - 转储堆栈跟踪。转储完成后,您删除断点并继续执行。(这一切都发生在运行时使用这个 API,没有黑魔法:))

于 2015-01-29T17:58:30.883 回答
0

一个技巧是:如果可以修改您的 API 以返回 aReader而不是 a ,那么您可以使用自定义( ,比如说 )File包装一个 String ,在它真正工作之前用 a覆盖各种方法。当然,仅在测试期间。 StringReaderclass SlowAsRubyStringReader extends Readerint read()Thread.sleep(500)

@see http://docs.oracle.com/javase/7/docs/api/java/io/StringReader.html

我认为这里有一个更大的问题,而不仅仅是 Files :您想检查在测试用例期间调用 API 的上下文,不是吗?也就是说,您希望能够检查堆栈并说:“啊哈!我发现您从 JustTookABath 对象调用 MudFactory API,太离谱了!”。如果是这种情况,那么您可能必须深入研究动态代理,这将允许您劫持函数调用或使用面向方面的编程,这允许您以更系统的方式做同样的事情。见http://en.wikipedia.org/wiki/Pointcut

于 2015-02-01T12:40:03.960 回答
0

您可以有一个单独的线程监视文件访问时间的更改,并在发生这种情况时生成一个 jvm 线程转储。至于在代码中生成线程转储,我没有尝试过,但看起来在这里得到了回答:Generate a Java thread dump without restarting。

我不知道这对你的线程之间的时间有多大的影响,但我想这应该非常接近。我也不是 100% 了解这个解决方案的操作系统独立性,因为我没有测试过它,但它应该适用于大多数现代系统。请参阅 java.nio.file.attribute.BasicFileAttributes 上的 javadocs 以了解如果不支持会返回什么。

于 2015-01-27T20:23:32.093 回答
-1

read()快速潜入本机代码,因此可能需要在该级别进行本机阻塞。或者,您可能需要考虑在代码中的点之前或之后记录堆栈跟踪read()

就像是:

log ( ExceptionUtils.getStackTrace(new Exception()) );

ExceptionUtils 文档在这里:https ://commons.apache.org/proper/commons-lang/javadocs/api-3.1/org/apache/commons/lang3/exception/ExceptionUtils.html

于 2015-01-21T00:00:16.730 回答