我不确定,但我非常确定我在Oracle Java实现(我可以证实受影响的1.7.0_67和1.8.0_31 )中发现了一个bug (或未记录的特性)。
The Symptom
当管道已满时,写入管道的等待时间可能比管道再次空闲所需的时间长一秒。这个问题的最小示例如下(我已经将此处显示的示例推送到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();
}
}
}
只有在超时(如上图所示的1000毫秒)或调用notifyAll()
时,才会中断wait(1000)
,这只会在以下情况下发生:
在awaitSpace()
中的
wait(1000)
之前,正如我们在receivedLast()
中的Question
有没有人有足够的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)
https://stackoverflow.com/questions/28617175
复制相似问题