NIO的通道
NIO的通道是IO基础上的创新,类似于原I/O中的流,它通过和缓冲区结合,可以使用最小的开销来访问操作系统本身的I/O服务。
目前已知的Channel的实现类有这些:
- FileChannel
- DatagramChannel
- SocketChannel
- ServerSocketChannel
来看一下FileChannel通道的继承关系图
先来看看Channel类代码的实现:
1 | public interface Channel extends Closeable { |
发现通道都是用接口实现的,然后只有两个方法,一个打开,一个关闭。至于为什么要用接口,对于不同的通道有不同的打开或者关闭操作。子接口ReadableByteChannel和WritableByteChannel的read和write方法都是基于ByteBuffer,基于字节来操作的。
再来看下ReadableByteChannel和WritableByteChannel的代码:
1 | public interface ReadableByteChannel extends Channel { |
1 | public interface WritableByteChannel |
通道通过继承ReadableByteChannel和WritableByteChannel使得可以进行读和写操作。
经典的读和写操作:
1 | private static void read(String filename) throws IOException { |
写如果要附加到源文件末尾可以这样写:
1 | private static void addToEnd(String filename) throws IOException { |
也可以这样子:通过设置append模式。
1 | /** |
FileChannel源码实现
read方法
下面重点看看FileChannel的源码:
FileChannel的read方法是通过FileChannelImpl来实现的:
1 | public int read(ByteBuffer var1) throws IOException { |
可以看到,read方法通过调用IOUtil.read方法来进行读的。
1 | static int read(FileDescriptor var0, ByteBuffer var1, long var2, NativeDispatcher var4) throws IOException { |
在read方法中,会调用getTemporaryDirectBuffer方法申请DirectBuffer堆内存,然后通过readIntoNativeBuffer方法将数据读取到缓冲区var5,读取完之后在复制到var1。
1 | private static int readIntoNativeBuffer(FileDescriptor var0, ByteBuffer var1, long var2, NativeDispatcher var4) throws IOException { |
可以看到,底层是通过NativeDispatcher的read方法来实现的。
write方法
write方法和read方法的实现大体差不多:
1 | public int write(ByteBuffer var1) throws IOException { |
通过调用IOUtil的write方法将buffer中的数据进行处理。
1 | static int write(FileDescriptor var0, ByteBuffer var1, long var2, NativeDispatcher var4) throws IOException { |
这里也是申请了一块DirectBuffer,将bytebuffer中数据放到var8中,调用writeFromNativeBuffer方法来实现写操作。
1 | private static int writeFromNativeBuffer(FileDescriptor var0, ByteBuffer var1, long var2, NativeDispatcher var4) throws IOException { |
其底层都是通过NativeDispatcher类中的write方法来实现写操作。
总结
- read方法导致数据复制了两次。
- write方法也导致数据复制了两次。