当我使用Channels.newChannel(是)从InputStream创建一个通道时,java标准库返回一个ReadableByteChannelImpl,它是:
private static class ReadableByteChannelImpl
extends AbstractInterruptibleChannel // Not really interruptible
implements ReadableByteChannel
{
InputStream in;
private static final int TRANSFER_SIZE = 8192;
private byte buf[] = new byte[0];
private boolean open = true;
private Object readLock = new Object();
ReadableByteChannelImpl(InputStream in) {
this.in = in;
}
public int read(ByteBuffer dst) throws IOException {
int len = dst.remaining();
int totalRead = 0;
int bytesRead = 0;
synchronized (readLock) {
while (totalRead < len) {
int bytesToRead = Math.min((len - totalRead),
TRANSFER_SIZE);
if (buf.length < bytesToRead)
buf = new byte[bytesToRead];
if ((totalRead > 0) && !(in.available() > 0))
break; // block at most once
try {
begin();
bytesRead = in.read(buf, 0, bytesToRead);
} finally {
end(bytesRead > 0);
}
if (bytesRead < 0)
break;
else
totalRead += bytesRead;
dst.put(buf, 0, bytesRead);
}
if ((bytesRead < 0) && (totalRead == 0))
return -1;
return totalRead;
}
}
protected void implCloseChannel() throws IOException {
in.close();
open = false;
}
}
正如你所看到的那样,它在第一次调用read(ByteBuffer dst)时会阻塞,并且永远不会再次阻塞。参见:
if ((totalRead > 0) && !(in.available() > 0))
break; // block at most once
这种奇怪行为背后的原因是什么?
另外,在没有实际使这个频道真正可以中断的情况下扩展AbstractInterruptibleChannel的动机是什么?
答案 0 :(得分:1)
如果它已经读取至少一个字节并且基础流宣布没有字节available,它将不会阻止。请注意,即使某些字节可用,InputStream#available()
也可以返回零,但不应该承诺的字节数超过可以读取的而不会阻塞。因此,这个ReadableByteChannel
努力读取至少一个字节 - 假设提供的ByteBuffer
有至少一个字节的空间 - 并且这样做,将不会尝试读取基础流再次,除非流承诺更多字节可用而不阻塞。
至于为什么ReadableByteChannelImpl
延伸AbstractInterruptibleChannel
,我怀疑这是为了确保在调用Channel#close()
时正确关闭包裹的InputStream
,其合同将进一步细化InterruptibleChannel#close()
。扩展AbstractInterruptibleChannel
允许ReadableByteChannelImpl
借用其线程安全的已开启状态的守卫。
正如你所说,这是一个虚假的广告,不是真正可以中断,但它容忍从一个单独的线程关闭,并使这样做是幂等的。