java io和java nio区别:前者是以流的方式完成IO操作,所有的IO都是单个字节在流动,stream对象一次移动一个字节.后者是JDK1.4后出现的,有如下特性:
1,为所有原始类型提供buffer支持
2.字符集编码解决方案
3.支持锁和内存映射文件的文件访问接口
4.提供多路non-blocking非阻塞式的高伸缩性网路IO
NIO包引入四个关键的抽象数据类型:
Buffer:缓冲区是特定基本类型元素的线性有限序列。除内容外,缓冲区的基本属性还包括容量、限制和位置.多个当前线程使用缓冲区是不安全的。如果一个缓冲区由不止一个线程使用,则应该通过适当的同步来控制对该缓冲区的访问
Charset:定义用来在字节和 Unicode 字符之间转换的 charset、解码器和编码器
Channel:包括file,pipe和socket三种双向交流的通道 .
Selector:将多元异步IO操作集中到一个或多个线程中
一. buffer和channel:
channel和buffer是NIO中核心对象.channel是对原IO中流的模拟,所有数据都要通过通道进行传输,buffer实际上是一个内存容器,发给通道的对象都必须首先放到一个buffer中.
buffer:是一个对象,包含写入或读出的数据.IO是直接向stream中写入或读取数据,NIO是所有数据先写入或读取到buffer中.而buffer实际上是一个byte数据.总之:buffer就是一个连续的内存块,是NIO中数据读写的中转地.
ByteBuffer buff = ByteBuffer.allocate(1024);
ByteBuffer buff = ByteBuffer.wrap(new byte[length],0,length);
将文件映射到内存中:
MapppedByteBuffer mbb = FileChannel.map(FileChannel.MapMode.READ_WRITE, 0, 1024 );
将fileChaneel对应的文件的前1024字节文件映射内存中.
channel:也是一个对象,用于IO操作的连接.通道表示到实体,如硬件设备、文件、网络套接字或可以执行一个或多个不同 I/O 操作(如读取或写入)的程序组件的开放的连接.有打开和关闭两种状态.一般情况下是多线程访问channel是安全的.
FileChannel:用于读取、写入、映射和操作文件的通道.用于读取、写入、映射和操作文件的通道.
可从现有的 FileInputStream
、FileOutputStream
或 RandomAccessFile
对象获得文件通道,方法是调用该对象的 getChannel 方法,这会返回一个连接到相同底层文件的文件通道.
FileInputStream.getChannel() ;
FileOutputStream.getChannel();
RandomAccessFile.getChannel();
除了字节通道中常见的读取、写入和关闭操作外,此类还定义了下列特定于文件的操作:
以不影响通道当前位置的方式,对文件中绝对位置的字节进行读取
或
写入
。
将文件中的某个区域直接映射
到内存中;对于较大的文件,这通常比调用普通的 read 或 write 方法更为高效。
强制
对底层存储设备进行文件的更新,确保在系统崩溃时不丢失数据。
以一种可被很多操作系统优化为直接向文件系统缓存发送或从中读取的高速传输方法,将字节从文件传输到某个其他通道
中,
反之亦然
。
可以锁定
某个文件区域,以阻止其他程序对其进行访问。
锁定某个文件区域引入FileLock对象.每次通过FileChannel.lock/tryLock方法获得文件锁对象.它要么是独占的,要么是共享的.
文件锁定以整个 Java 虚拟机来保持。但它们不适用于控制同一虚拟机内多个线程对文件的访问.多个并发线程可安全地使用文件锁定对象。
要获得独占锁,文件须以写方式打开:
RandomAccessFile raf = new RandomAccessFile( "filelocks.txt", "rw" );
FileChannel fc = raf.getChannel();
FileLock lock = fc.lock( start, end, false );
锁释放: release方法,终止JVM.
SocketChannel:针对面向流的连接套接字的可选择通道。
DatagramChannel:针对面向数据报套接字的可选择通道,多个并发线程可安全地使用套接字通道。尽管在任意给定时刻最多只能有一个线程进行读取和写入操作,但数据报通道支持并发的读写.
二.CharSet和Selector
CharSet:字符集.java中使用unicode编码,每个字符中占两个字节.向ByteBuffer中写入数据时要考虑字符集的编码方式encode,读取数据时要考虑字符集的解码方式decode.
得到一个CharSet实例:
CharSet cs = CharSet.forName("utf-8");
CharSet cs = CharSet.defaultCharSet();
得到charset对象后需要创建编码对象和解码对象
CharSetDecoder decoder = cs.newDecoder();
CharSetEncoder encoder= cs.newEncoder();
编码:
CharBuffer cb = decoder.decode(ByteBuffer buff);
解码:
ByteBuffer buff = encoder.encode(cb);
Selector:SelectableChannel
对象的多路复用器。 异步IO的核心对象就是Selector.它是注册各种IO事件的地方,且当事件发生时,它会告诉你所发生的事件.
Selector selector = Selector.open();
SocketChannel sc = SocketChannel.open(new InetSocketAddress("localhost",8088));
//多路复用时设置为非阻塞的.
sc.configBolcking(false);
//选择注册键
SelectorKey sk = sc.register(selector,SelectorKey.OP_CONNECT);//SelectionKey.OP_READ,OP_WRITE,ACCEP
int nums = selector.select();//择一组键,其相应的通道已为 I/O 操作准备就绪。
此方法执行处于阻塞模式的选择操作。仅在至少选择一个通道、调用此选择器的 wakeup
方法,或者当前的线程已中断(以先到者为准)后此方法才返回
这个方法会阻塞,直到至少有一个已注册的事件发生
//返回选择键集合
Set selectedKeys = selector.selectedKeys();
//遍历set,记得处理完将该item 删除.remove
//
int nums = selector.select();
System.out.println(nums);
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> it = selectedKeys.iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
// handle key
it.remove();
}