简体   繁体   中英

Java's ReadableByteChannelImpl inconsistent behaviour

When I create a channel out of the InputStream with Channels.newChannel(is) java standard library returns a ReadableByteChannelImpl, which is:

   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;
       }
   }

As you can see it blocks when calling read(ByteBuffer dst) for the first time, and never blocks again. See:

           if ((totalRead > 0) && !(in.available() > 0))
               break; // block at most once

What is the reason behind such a weird behavior?

Also, what is the motivation for extending AbstractInterruptibleChannel without actually making this channel truly interruptible ?

It will not block if it's already read at least one byte and the underlying stream announces that no bytes are available . Note that InputStream#available() can return zero even when some bytes are available, but it should not promise more bytes than can be read without blocking . Hence, this ReadableByteChannel makes an effort to read at least one byte -- assuming the provided ByteBuffer has room for at least one byte -- and, having done so, will not attempt to read the underlying stream again unless the stream is promising that more bytes are available without blocking.

As for why ReadableByteChannelImpl extends AbstractInterruptibleChannel , I suspect that it's to ensure that the wrapped InputStream will be closed properly upon calling Channel#close() , whose contract is further refined by InterruptibleChannel#close() . Extending AbstractInterruptibleChannel allows ReadableByteChannelImpl to borrow its thread-safe opened-closed state guards.

It is a bit of false advertising, as you say, not being truly interruptible, but it is tolerant of being closed from a separate thread, and makes doing so idempotent.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM