【发布时间】:2015-02-19 21:23:08
【问题描述】:
我不确定,但我很确定我在 Oracle Java 实现中发现了一个错误(或未记录的功能)(1.7.0_67 和 1.8.0_31 我可以验证为受影响)。
症状
当管道已满时,对管道的写入可能会比管道再次空闲所需的时间多等待一秒钟。问题的一个最小示例如下(我已将此处显示的示例推送到a repository on GitHub):
private static void threadA() throws IOException, InterruptedException {
logA("Filling pipe...");
pos.write(new byte[5]);
logA("Pipe full. Writing one more byte...");
pos.write(0);
logA("Done.");
}
private static void threadB() throws IOException, InterruptedException {
logB("Sleeping a bit...");
Thread.sleep(100);
logB("Making space in pipe...");
pis.read();
logB("Done.");
}
pis 和 pos 分别连接 PipedInputStream 和 PipedOutputStream 实例。 logA 和 logB 是辅助函数,它们输出线程名称(A 或 B)、时间戳(以毫秒为单位)和消息。输出如下:
0 A: Filling pipe...
6 B: Sleeping a bit...
7 A: Pipe full. Writing one more byte...
108 B: Making space in pipe...
109 B: Done.
1009 A: Done.
可以看到,B: Done 和 A: Done 之间有一秒(1000 毫秒)。这是由于Oracle Java 1.7.0_67中PipedInputStream的实现导致的,如下:
private void awaitSpace() throws IOException {
while (in == out) {
checkStateForReceive();
/* full: kick any waiting readers */
notifyAll();
try {
wait(1000);
} catch (InterruptedException ex) {
throw new java.io.InterruptedIOException();
}
}
}
wait(1000) 仅在达到超时(1000 毫秒,如上所示)或调用 notifyAll() 时才会中断,这仅在以下情况下发生:
- 在
awaitSpace()中,在wait(1000)之前,我们可以在上面的sn-p 中看到 - 在
receivedLast()中,流关闭时调用(此处不适用) - 在
read()中,但仅当read()正在等待空缓冲区填满时——此处也不适用
问题
有没有人有足够的 Java 经验告诉我这是否应该是预期的行为? PipedOutputStream.write(...) 使用方法awaitSpace() 来等待空闲空间,他们的合约简单地声明:
此方法阻塞,直到所有字节都写入输出流。
虽然严格没有违反这一点,但 1 秒的等待时间似乎很长。如果我要解决这个问题(最小化/降低等待时间),我建议在每次读取结束时插入 notifyAll() 以确保等待的作者得到通知。为了避免额外的同步时间开销,可以使用一个简单的布尔标志(并且不会损害线程安全)。
受影响的 Java 版本
到目前为止,我可以在 Java 7 和 Java 8 上验证这一点,确切地说是以下版本:
$ java -version
java version "1.7.0_67"
Java(TM) SE Runtime Environment (build 1.7.0_67-b01)
Java HotSpot(TM) 64-Bit Server VM (build 24.65-b04, mixed mode)
$ java -version
java version "1.8.0_31"
Java(TM) SE Runtime Environment (build 1.8.0_31-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.31-b07, mixed mode)
【问题讨论】:
-
是的,这听起来像是一个错误。
标签: java