首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

NIO Channel和Buffer(3)

NIO Channel和Buffer(3)

FileChannel
A channel for reading, writing, mapping, and manipulating a file.
一个用来写、读、映射和操作文件的通道。
FileChannel的read、write和map通过其实现类FileChannelImpl实现。
  • read实现
    public int read(ByteBuffer dst) throws IOException {  ensureOpen();  if (!readable)      throw new NonReadableChannelException();  synchronized (positionLock) {      int n = 0;      int ti = -1;      try {          begin();          ti = threads.add();          if (!isOpen())              return 0;          do {              n = IOUtil.read(fd, dst, -1, nd);          } while ((n == IOStatus.INTERRUPTED) && isOpen());          return IOStatus.normalize(n);      } finally {          threads.remove(ti);          end(n > 0);          assert IOStatus.check(n);      }  }}FileChannelImpl的read方法通过IOUtil的read实现:
    static int read(FileDescriptor fd, ByteBuffer dst, long position,              NativeDispatcher nd) IOException {  if (dst.isReadOnly())      throw new IllegalArgumentException("Read-only buffer");  if (dst instanceof DirectBuffer)      return readIntoNativeBuffer(fd, dst, position, nd);  // Substitute a native buffer  ByteBuffer bb = Util.getTemporaryDirectBuffer(dst.remaining());  try {      int n = readIntoNativeBuffer(fd, bb, position, nd);      bb.flip();      if (n > 0)          dst.put(bb);      return n;  } finally {      Util.offerFirstTemporaryDirectBuffer(bb);  }}通过上述实现可以看出,基于channel的文件数据读取步骤如下:
    1、申请一块和缓存同大小的DirectByteBuffer bb。
    2、读取数据到缓存bb,底层由NativeDispatcher的read实现。
    3、把bb的数据读取到dst(用户定义的缓存,在jvm中分配内存)。
    read方法导致数据复制了两次。
  • write实现
    public int write(ByteBuffer src) throws IOException {  ensureOpen();  if (!writable)      throw new NonWritableChannelException();  synchronized (positionLock) {      int n = 0;      int ti = -1;      try {          begin();          ti = threads.add();          if (!isOpen())              return 0;          do {              n = IOUtil.write(fd, src, -1, nd);          } while ((n == IOStatus.INTERRUPTED) && isOpen());          return IOStatus.normalize(n);      } finally {          threads.remove(ti);          end(n > 0);          assert IOStatus.check(n);      }  }}和read实现一样,FileChannelImpl的write方法通过IOUtil的write实现:
    static int write(FileDescriptor fd, ByteBuffer src, long position,               NativeDispatcher nd) throws IOException {  if (src instanceof DirectBuffer)      return writeFromNativeBuffer(fd, src, position, nd);  // Substitute a native buffer  int pos = src.position();  int lim = src.limit();  assert (pos <= lim);  int rem = (pos <= lim ? lim - pos : 0);  ByteBuffer bb = Util.getTemporaryDirectBuffer(rem);  try {      bb.put(src);      bb.flip();      // Do not update src until we see how many bytes were written      src.position(pos);      int n = writeFromNativeBuffer(fd, bb, position, nd);      if (n > 0) {          // now update src          src.position(pos + n);      }      return n;  } finally {      Util.offerFirstTemporaryDirectBuffer(bb);  }}通过上述实现可以看出,基于channel的文件数据写入步骤如下:
    1、申请一块DirectByteBuffer,bb大小为byteBuffer中的limit - position。
    2、复制byteBuffer中的数据到bb中。
    3、把数据从bb中写入到文件,底层由NativeDispatcher的write实现,具体如下:
    private static int writeFromNativeBuffer(FileDescriptor fd,       ByteBuffer bb, long position, NativeDispatcher nd)  throws IOException {  int pos = bb.position();  int lim = bb.limit();  assert (pos <= lim);  int rem = (pos <= lim ? lim - pos : 0);  int written = 0;  if (rem == 0)      return 0;  if (position != -1) {      written = nd.pwrite(fd,                          ((DirectBuffer)bb).address() + pos,                          rem, position);  } else {      written = nd.write(fd, ((DirectBuffer)bb).address() + pos, rem);  }  if (written > 0)      bb.position(pos + written);  return written;}write方法也导致了数据复制了两次
返回列表