【Java8源码分析】NIO包-FileChannel
来源:互联网 发布:relief算法应用 编辑:程序博客网 时间:2024/05/28 15:03
转载请注明出处:http://blog.csdn.net/linxdcn/article/details/72910485
1 概述
Java NIO 由以下几个核心部分组成:
- Buffer
- Channel
- Selectors
相关类的使用方法可以参考Java NIO 系列教程,写的通俗易懂。
本文主要从源码方面分析一下Channel类。
2 Channel介绍
Java NIO的通道类似流,但又有些不同:
- 既可以从通道中读取数据,又可以写数据到通道。但流的读写通常是单向的。
- 通道可以异步地读写。
- 通道中的数据总是要先读到一个Buffer,或者总是要从一个Buffer中写入。
Java NIO中最重要的通道的实现有以下几种
- FileChannel:从文件中读写数据
- DatagramChannel:能通过UDP读写网络中的数据
- SocketChannel:能通过TCP读写网络中的数据
- ServerSocketChannel:可以监听新进来的TCP连接,像Web服务器那样。对每一个新进来的连接都会创建一个SocketChannel
在Java中,Channel是一个简单的接口,只有两个函数,如下所示
public interface Channel extends Closeable { public boolean isOpen(); public void close() throws IOException;}
3 FileChannel
FileChannel主要是用来读、写和映射一个系统文件的Channel,它是一个抽象类,具体由FileChannelImpl实现。
public abstract class FileChannel { // 构造函数 protected FileChannel() { } // 写入buffer public abstract int read(ByteBuffer dst) throws IOException; public abstract long read(ByteBuffer[] dsts, int offset, int length) throws IOException; public final long read(ByteBuffer[] dsts) throws IOException { return read(dsts, 0, dsts.length); } // 从buffer读取 public abstract int write(ByteBuffer src) throws IOException; public abstract long write(ByteBuffer[] srcs, int offset, int length) throws IOException; public final long write(ByteBuffer[] srcs) throws IOException { return write(srcs, 0, srcs.length); }}
3 FileChannelImpl
3.1 打开通道
有两种方法可以打开一个FileChannel,类似于打开一个字节流文件
(1)通过InputStream、OutputStream或RandomAccessFile来获取一个FileChannel(常用),调用其中一个得getChannel函数即可
public FileChannel getChannel() { synchronized (this) { if (channel == null) { channel = FileChannelImpl.open(fd, true, false, this); } return channel; } }
(2)通过FileChannel的静态方法,直接打开
public static FileChannel open(Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs) throws IOException{ FileSystemProvider provider = path.getFileSystem().provider(); return provider.newFileChannel(path, options, attrs);}
3.2 read
public int read(ByteBuffer dst) throws IOException { ensureOpen(); if (!readable) throw new NonReadableChannelException(); // 1 写入缓冲区需要加锁 synchronized (positionLock) { int n = 0; int ti = -1; try { begin(); ti = threads.add(); if (!isOpen()) return 0; do { // 2 通过IOUtil.read实现 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); } }}
主要步骤如下:
- 写入缓冲区的操作需要加锁,保证多线程安全
- 临界区中通过
IOUtil
实现向buffer的写入,IOUtil的read函数如下
static int read(FileDescriptor fd, ByteBuffer dst, long position, NativeDispatcher nd) throws IOException{ // 1 申请一块临时堆外DirectByteBuffer ByteBuffer bb = Util.getTemporaryDirectBuffer(dst.remaining()); try { // 2 先往DirectByteBuffer写入数据,提高效率 int n = readIntoNativeBuffer(fd, bb, position, nd); bb.flip(); if (n > 0) // 3 再拷贝到传入的buffer dst.put(bb); return n; } finally { Util.offerFirstTemporaryDirectBuffer(bb); }}
主要步骤如下:
- 申请一块临时堆外DirectByteBuffer,大小同传入的buffer,不了解DirectByteBuffer的可以参考内存映射文件DirectByteBuffer与MappedByteBuffer一文
- 先往DirectByteBuffer写入数据,这样能提高效率
- 再把DirectByteBuffer数据拷贝到用户传入的buffer
3.3 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 { // 2 通过IOUtil.write实现 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); } }}
主要步骤如下:
- 读取缓冲区的操作需要加锁,保证多线程安全
- 临界区中通过
IOUtil
实现从buffer的读取,IOUtil的write函数如下
static int write(FileDescriptor fd, ByteBuffer src, long position, NativeDispatcher nd) throws IOException{ // 1 如果传入的缓冲区是DirectBuffer,直接从里面读取 if (src instanceof DirectBuffer) return writeFromNativeBuffer(fd, src, position, nd); int pos = src.position(); int lim = src.limit(); assert (pos <= lim); int rem = (pos <= lim ? lim - pos : 0); // 2 否则构造一块跟传入缓冲区一样大小的DirectBuffer ByteBuffer bb = Util.getTemporaryDirectBuffer(rem); try { bb.put(src); bb.flip(); src.position(pos); // 3 调用writeFromNativeBuffer读取 int n = writeFromNativeBuffer(fd, bb, position, nd); if (n > 0) { // now update src src.position(pos + n); } return n; } finally { Util.offerFirstTemporaryDirectBuffer(bb); }}
主要步骤如下:
- 判断传入的缓冲区是否是DirectByteBuffer,是的话直接读取
- 否则构造一块跟传入缓冲区一样大小的DirectBuffer
- 调用writeFromNativeBuffer读取
4 总结
(1)虽然FileChannel在NIO包中,但是FileChannel的读取只能是阻塞的,而像DatagramChannel、SocketChannel则可设置为阻塞的。
5 参考
http://ifeve.com/file-channel/
转载请注明出处:http://blog.csdn.net/linxdcn/article/details/72910485
阅读全文
0 0
- 【Java8源码分析】NIO包-FileChannel
- 【Java8源码分析】NIO包-Selector选择器
- java8 NIO FileChannel例
- 【Java8源码分析】NIO包-Buffer类:ByteBuffer与HeapByteBuffer(一)
- 【Java8源码分析】NIO包-Buffer类:内存映射文件DirectByteBuffer与MappedByteBuffer(二)
- Nio-FileChannel与粘包问题
- 【Java8源码分析】并发包-AtomicInteger
- 【Java8源码分析】并发包-CopyOnWriteArrayList
- 【Java8源码分析】locks包-AbstractQueuedSynchronizer同步器
- 【Java8源码分析】locks包-ReentrantLock
- 【Java8源码分析】locks包-ReentrantReadWriteLock
- 【Java8源码分析】并发包-Semaphore
- 【Java8源码分析】并发包-CountDownLatch
- 【Java8源码分析】并发包-CyclicBarrier
- java.nio.channels.FileChannel源码解读
- NIO - FileChannel
- NIO - FileChannel
- NIO - FileChannel
- AT6559 北斗导航 BDS/GNSS全星座SOC芯片规格书替代泰斗、U-BLOX芯片
- POJ3614-number1
- 特征工程
- BEM命名方式
- 2017-Java面试必看问题之一
- 【Java8源码分析】NIO包-FileChannel
- 关于Error: ARCH: Unsupported platform.
- JSOUP学习笔记
- 广播接收者之有序广播
- Android Binder Java
- Java分布式应用技术架构介绍
- 如何利用jqGrid表格方法重新设置caption属性值
- 在不使用额外空间的前提下,将数组的偶数放到数组的奇数前面 | Python
- Codeforces Round #418 (Div. 2) 题解 ABC