NIO
来源:互联网 发布:天意网络魔域一条龙 编辑:程序博客网 时间:2024/06/05 06:53
NIO最重要的组件是Buffer与Channel。Buffer是一个抽象类,原生类型都有一个Buffer(int,char,float,Double等)
NIO的Buffer提供了一个可以直接访问物理内存的类DirectBuffer,DirectBuffer继承自ByteBuffer。普通的ByteBuffer在JVM堆上分空间,收到最大堆的限制。但DirectBuffer直接分配在物理内存中。DirectBuffer替代了传统ByteBuffer的“内核缓冲”,更接近系统底层,故读写更快。但是开辟与销毁的时间交传统buffer更长。GC只记录了堆空间内存的回收,但DirectBuffer占用的空间不在堆中,故GC信息不可见。(Java程序性能优化 P118)
System.arraycopy是native的函数,数组复制效率最高。
Object的clone方法可以绕过对象的construct,但是在默认情况时,clone是浅拷贝。
对于一些工具类,应该使用static方法,较实例方法更快,因为缺少了多态的虚表;static方法也不需要生成实例。调用更易。
标准的IO基于字节流和字符流进行操作的,而NIO是基于通道(Channel)和缓冲区(Buffer)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。
所有的 IO 在NIO 中都从一个Channel 开始。Channel 有点象流。
JAVA NIO中的一些主要Channel的实现:
FileChannel从文件中读写数据。
DatagramChannel UDP
SocketChannel TCP
ServerSocketChannel监听新进来的TCP连接,像Web服务器那样。对每一个新进来的连接都会创建一个SocketChannel。
Selector允许单线程处理多个 Channel。如果你的应用打开了多个连接(通道),但每个连接的流量都很低,使用Selector就会很方便。
Java NIO的Channel通道类似流,但又有些不同:(双向,异步,带buffer)
既可以从通道中读取数据,又可以写数据到通道。但流的读写通常是单向的。
通道可以异步地读写。
通道中的数据总是要先读到一个Buffer,或者总是要从一个Buffer中写入。
使用Buffer读写数据一般遵循以下四个步骤:
写入数据到Buffer
调用flip()方法:切换模式,flip方法将Buffer从写模式切换到读模式。调用flip()方法会将position设回0,并将limit设置成之前position的值。
从Buffer中读取数据
调用clear()方法或者compact()方法,清空,或清空已读
当向buffer写入数据时,buffer会记录下写了多少数据。一旦要读取数据,需要通过flip()方法将Buffer从写模式切换到读模式。在读模式下,可以读取之前写入到buffer的所有数据。
一旦读完了所有的数据,就需要清空缓冲区,让它可以再次被写入。有两种方式能清空缓冲区:调用clear()或compact()方法。clear()方法会清空整个缓冲区。compact()方法只会清除已经读过的数据。任何未读的数据都被移到缓冲区的起始处,新写入的数据将放到缓冲区未读数据的后面。
从通道读取字节到ByteBuffer。当这个方法调用返回时,你不知道你所需的所有数据是否在缓冲区内。你所知道的是,该缓冲区包含一些字节。
若buffer需多次被读入不同的channel,就需rewind()方法:
Buffer.rewind()将position设回0,所以你可以重读Buffer中的所有数据。limit保持不变,仍然表示能从Buffer中读取多少个元素(byte、char等)。
scatter/gather:
scatter:
ByteBuffer header =ByteBuffer.allocate(128);
ByteBuffer body = ByteBuffer.allocate(1024);
ByteBuffer[] bufferArray = { header, body};
channel.read(bufferArray);
gather:
ByteBuffer header =ByteBuffer.allocate(128);
ByteBuffer body = ByteBuffer.allocate(1024);
//write data into buffers
ByteBuffer[] bufferArray = { header, body};
channel.write(bufferArray);
在channel中,使用transferTo,transferFrom 可以将一个通道的数据传送至另一个通道。NIO复制
RandomAccessFile fromFile = newRandomAccessFile("fromFile.txt", "rw");
FileChannel fromChannel = fromFile.getChannel();
RandomAccessFile toFile = newRandomAccessFile("toFile.txt", "rw");
FileChannel toChannel = toFile.getChannel();
long position = 0;
long count = fromChannel.size();
toChannel.transferFrom(position, count,fromChannel);
selector:
用单个线程来处理多个Channels的好处是,线程之间上下文切换的开销很大。
为了将Channel和Selector配合使用,必须将channel注册到selector上。通过SelectableChannel.register()方法来实现,如下:
channel.configureBlocking(false);
SelectionKey key =channel.register(selector, Selectionkey.OP_READ);
与Selector一起使用时,Channel必须处于非阻塞模式下。这意味着不能将FileChannel与Selector一起使用,因为FileChannel不能切换到非阻塞模式。而套接字通道都可以。
注意register()方法的第二个参数。这是一个“interest集合”,意思是在通过Selector监听Channel时对什么事件感兴趣。可以监听四种不同类型的事件:连接,接受,读写
Connect
Accept
Read
Write
close():用完Selector后调用其close()方法会关闭该Selector,且使注册到该Selector上的所有SelectionKey实例无效。通道本身并不会关闭。
FileChannel:
FileChannel无法设置为非阻塞模式,它总是运行在阻塞模式下。
通过使用一个InputStream、OutputStream或RandomAccessFile来获取一个FileChannel实例。
FileChannel实例的size()方法将返回该实例所关联文件的大小;
使用FileChannel.truncate()方法截取一个文件。截取文件时,文件将中指定长度后面的部分将被删除。channel.truncate(1024);
FileChannel.force()方法将通道里尚未写入磁盘的数据强制写到磁盘上。
RandomAccessFile:
RandomAccessFile是用来访问那些保存数据记录的文件的,你就可以用seek( )方法来访问记录,并进行读写了。RandomAccessFile不属于InputStream和OutputStream类系的。
只有RandomAccessFile才有seek搜寻方法,而这个方法也只适用于文件。
RandomAccessFile的绝大多数功能,但不是全部,已经被JDK 1.4的nio的"内存映射文件(memory-mapped files)"给取代了,应该用"内存映射文件"来代替RandomAccessFile了。
- nio
- NIO
- NIO
- nio
- NIO
- NIO
- nio
- Nio
- NIO
- NIO
- NIO
- nio
- NIO
- NIO
- NIO
- NIO
- NIO
- NIO
- ListView异步加载图片出现图片错位的解决方案
- Android 调用系统相机部分源码分析
- 数组常用方法
- Lightoj 1122【计数DP】
- MySQL连接失败:Can't connect to MySQL server on '127.0.0.1'
- NIO
- Redis 事务
- CodeForces 3 D.Least Cost Bracket Sequence(贪心+优先队列)
- 如何在字符串的长时间("2017-07-18 00:00:00")上对短时间("yyyy-mm-dd")分组和排序
- ble之gatt server
- 位运算实现加减乘除四则运算
- Codeforces 825D Suitable Replacement【贪心】水题
- Git for windows 配置
- 内存映射文件:MappedByteBuffer