目录
1.nio与io的区别
-
NIO是以块的方式处理数据,但是IO是以最基础的字节流的形式一次一个字节地去写入和读出的。所以在效率上的话,肯定是NIO效率比IO效率会高出很多。
-
NIO不在是和IO一样用OutputStream和InputStream 输入流的形式来进行处理数据的,但是又是基于这种流的形式,而是采用了通道和缓冲区的形式来进行处理数据的。
-
NIO的通道是可以双向的,但是IO中的流只能是单向的
2.Channel
-
Channel,国内大多翻译成“通道”。Channel和IO中的Stream(流)是差不多一个等级的。只不过Stream是单向的,譬如:InputStream, OutputStream.而Channel是双向的,既可以用来进行读操作,又可以用来进行写操作Java NIO中的FileChannel是一个连接到文件的通道。可以通过文件通道读写文件
3.Buffer
-
Buffer是一个缓冲区实质上是一个数组,用于和NIO通道FileChannel进行交互。
-
在 I/O 中,将数据直接写入或者将数据直接读到 Stream 对象中。
-
在 NIO 库中,所有数据都是用Buffer 缓冲区处理的。在读取数据时,它是直接读到缓冲区中的。在写入数据时,它是写入到缓冲区中的。任何时候访问 NIO 中的数据,都是将它放到缓冲区中。
-
向Buffer中写数据
-
从Channel写到Buffer (fileChannel.read(buf))
-
通过Buffer的put()方法 (buf.put(…))
-
-
从Buffer中读取数据
-
从Buffer读取到Channel (channel.write(buf))
-
使用get()方法从Buffer中读取数据 (buf.get())
把Buffer简单地理解为一组基本数据类型的元素列表,它通过几个变量来保存这个数据的当前位置状态
-
-
案例
import java.nio.ByteBuffer;
public class FileWriteWithNIO {
public void writeWithNIO() {
//创建ByteBuffer对象
ByteBuffer buffer = ByteBuffer.allocate(12);
buffer.put("hello".getBytes()); //buffer写入数据
System.out.printf("capacity:%s,limit:%s,position:%s\n",
buffer.capacity(), buffer.limit(), buffer.position());
System.out.println();
System.out.println("-------------------------------");
//position位置归0
buffer.flip();
System.out.printf("capacity:%s,limit:%s,position:%s\n",
buffer.capacity(), buffer.limit(), buffer.position());
// byte[] array = buffer.array();
// String s = new String(array,0,buffer.limit());
// System.out.println(s);
buffer.put("12345".getBytes());
}
public void readWithNIO() {
ByteBuffer buffer = ByteBuffer.allocate(11);
buffer.put("12345".getBytes());
System.out.printf("capacity:%s,limit:%s,position:%s\n",
buffer.capacity(), buffer.limit(), buffer.position());
System.out.println("--------position归0-------");
buffer.flip();
System.out.printf("capacity:%s,limit:%s,position:%s\n",
buffer.capacity(), buffer.limit(), buffer.position());
for (int i = 0; i < buffer.limit(); i++) {
System.out.print((char) buffer.get(i));
}
}
public static void main(String[] args) {
new FileWriteWithNIO().writeWithNIO();
new FileWriteWithNIO().readWithNIO();
}
}
4.Channel与Buffer一起使用
-
FileChannel和Buffer一起用的步骤
-
使用FileChannel之前,必须先打开它。(但是,我们无法直接打开一个FileChannel,需要通过使用一个InputStream、OutputStream或RandomAccessFile来获取一个FileChannel实例)
-
FileChannel类里的方法:
-
-
用完FileChannel后必须将其关闭:
-
import java.io.FileInputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; public class FileWriterWithNIO { public static void main(String[] args) { FileInputStream fis = null; FileChannel fc = null; try { //创建FileInputStream对象 fis = new FileInputStream("data.txt"); //打开channel通道 fc = fis.getChannel(); //定义ByteBuffer对象 ByteBuffer buffer = ByteBuffer.allocate(2); //边读边输出字符 int len; while ((len = fc.read(buffer)) != -1) { buffer.flip();//position归0 // buffer.remaining(); // buffer.hasRemaining();//判断是否有值可取(原理:position<limit,返回true) while (buffer.hasRemaining()) { byte b = buffer.get();//取position位置的字节值,然后增加position的值 System.out.println((char) b); } buffer.clear(); // buffer.flip();//相当于buffer.clear();//让position归0,不然无法再往buffer添加数据,会造成死循环 } //关闭通道和流 } catch (IOException e) { e.printStackTrace(); } finally { try { if (fc != null) { fc.close(); } if (fis != null) { fis.close(); } } catch (IOException e) { e.printStackTrace(); } } } }
-
利用NIO进行文件拷贝(也可拷贝部分数据)的示例:
import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.nio.channels.FileChannel; public class TestFileCopyWithNIO { public static void main(String[] args) throws IOException { FileInputStream fis = new FileInputStream("data.txt"); FileOutputStream fos = new FileOutputStream("data1.txt"); //打开输入通道 FileChannel inChannel = fis.getChannel(); //打开一个输出通道 FileChannel outChannel = fos.getChannel(); //transferTo把输入流通到数据转移到输出流通道 inChannel.transferTo(0, inChannel.size(), outChannel); //关闭通道和流 inChannel.close(); outChannel.close(); fos.close(); fis.close(); } }
5.事件及nio的非阻塞读取
-
Java NIO的事件选择器允许一个单独的线程来监视多个输入通道,你可以注册多个通道使用一个选择器,然后使用一个单独的线程来“选择”通道:这些通道里已经有可以处理的输入,或者选择已准备写入的通道。这种选择机制,使得一个单独的线程很容易来管理多个通道
-
由于有了事件选择器,因此NIO可以以非阻塞的方式读取数据
-
NIO的这些特性在网络通讯方面非常有用。
6.NIO和IO的区别
-
Java NIO和IO的主要区别
-
面向Stream和面向Buffer
-
Java NIO和IO之间最大的区别是IO是面向流(Stream)的,NIO是面向块(buffer)的。
-
面向流意味着从流中一次可以读取一个或多个字节,拿到读取的这些做什么你说了算,这里没有任何缓存(这里指的是使用流没有任何缓存,接收或者发送的数据是缓存到操作系统中的,流就像一根水管从操作系统的缓存中读取数据)而且只能顺序从流中读取数据,如果需要跳过一些字节或者再读取已经读过的字节,你必须将从流中读取的数据先缓存起来。
-
面向块的处理方式有些不同,数据是先被读/写到buffer中的,根据需要你可以控制读取什么位置的数据。这在处理的过程中给用户多了一些灵活性,你需要额外做的工作是检查你需要的数据是否已经全部到了buffer中,你还需要保证当有更多的数据进入buffer中时,buffer中未处理的数据不会被覆盖。
-
-
阻塞IO和非阻塞IO
-
所有的Java IO流都是阻塞的,这意味着,当一条线程执行read()或者write()方法时,这条线程会一直阻塞知道读取到了一些数据或者要写出去的数据已经全部写出,在这期间这条线程不能做任何其他的事情
-
java NIO的非阻塞模式(Java NIO有阻塞模式和非阻塞模式,阻塞模式的NIO除了使用Buffer存储数据外和IO基本没有区别)允许一条线程从channel中读取数据,通过返回值来判断buffer中是否有数据,如果没有数据,NIO不会阻塞,因为不阻塞这条线程就可以去做其他的事情,过一段时间再回来判断一下有没有数据NIO的写也是一样的,一条线程将buffer中的数据写入channel,它不会等待数据全部写完才会返回,而是调用完write()方法就会继续向下执行
-
-
Selectors
Java NIO的selectors允许一条线程去监控多个channels的输入,你可以向一个selector上注册多个channel,然后调用selector的select()方法判断是否有新的连接进来或者已经在selector上注册时channel是否有数据进入。selector的机制让一个线程管理多个channel变得简单。