• 文章
  • 在线工具

易懂JavaNIO非阻塞流介绍

Java NIO 非阻塞IO 非阻塞流
894
Java NIO(New IO)简介
从Java1.4版本开始引入的一个新的IO API与原来的IO有同样的作用和目的,使用的方式不同。
NIO支持面向缓冲区基于通道的IO操作。NIO将以更加高效的方式进行文件读写操作。
  • 传统的 IO 流都是阻塞式的。也就是说,当一个线程调用 read() 或 write()时,该线程被阻塞,直到有一些数据被读取或写入,该线程在此期间不能执行其他任务。因此,在完成网络通信进行 IO 操作时,由于线程会阻塞,所以服务器端必须为每个客户端都提供一个独立的线程进行处理,当服务器端需要处理大量客户端时,性能急剧下降
  • Java NIO 是非阻塞模式的。当线程从某通道进行读写数据时,若没有数据可用时,该线程可以进行其他任务。线程通常将非阻塞 IO 的空闲时间用于在其他通道上执行 IO 操作,所以单独的线程可以管理多个输入和输出通道。因此,NIO 可以让服务器端使用一个或有限几个线程来同时处理连接到服务器端的所有客户端。


NIO和IO的区别
IO
NIO
面向流
面向缓冲区
阻塞IO
非阻塞IO
(无)
选择器
缓冲区(Buffer)简介
在JavaNIO中负责存取数据,缓冲区就是数组。用于存储不同的数据类型(除了boolean类型),提供了对应的类型的缓冲区。
ByteBuffer、CharBuffer、ShortBuffer、IntBuffer、LongBuffer、FloatBuffer、DoubleBuffer
  1. 缓冲区的核心属性:
  2.    capacity:容量,表示缓冲区大小,一旦声明不能改变
   limit:界限,表示缓冲区可操作做的数据大小,limit 后面不能进行读写
   position:位置,表示缓冲区中正在操作数据的位置。

   mark:标记,用于需要记录position的位置

2. 缓冲区的核心方法:  

  1.    ByteBuffer.allocate(int):设置缓冲区大小
  2.    put():存数据到缓冲区
  3.    get():读取缓冲区数据
  4.    flip():切换到读取模式
  5.    rewind():可重复读数据,回到最开始读取
  6.    clear():回到最开始状态(但是数据不会被清理,处于被遗忘状态,基本属性还原)
  7.    mark():标记当前的position位置。相当于备份
  8. 3. 直接缓冲区与非直接缓冲区:
    •         a.字节缓冲区要么是直接的,要么是非直接的。如果为直接字节缓冲区,则 Java  虚拟机会 尽最大努力直接在机 此缓冲区上执行本机 I/O  操作。也就是说,在每次调用基础操作系统的一个本机 I/O  操作之前(或之后),虚拟机都会尽量避免将缓冲区的内容复制到中间缓冲区中(或从中间缓冲区中复制内容)。
    •         b.直接字节缓冲区可以通过调用此类的 allocateDirect()  工厂方法 来创建。此方法返回的 缓冲区进行分配和取消分配所需成本通常高于非直接缓冲区 。直接缓冲区的内容可以驻留在常规的垃圾回收堆之外,因此,它们对应用程序的内存需求量造成的影响可能并不明显。所以,建议将直接缓冲区主要分配给那些易受基础系统的机 本机 I/O  操作影响的大型、持久的缓冲区。一般情况下,最好仅在直接缓冲区能在程序性能方面带来明显好处时分配它们
    •        c.直接字节缓冲区还可以过 通过FileChannel  的 map()  方法  将文件区域直接映射到内存中来创建 。该方法返回MappedByteBuffer  。Java  平台的实现有助于通过 JNI  从本机代码创建直接字节缓冲区。如果以上这些缓冲区中的某个缓冲区实例指的是不可访问的内存区域,则试图访问该区域不会更改该缓冲区的内容,并且将会在访问期间或稍后的某个时间导致抛出不确定的异常
    •        d.字节缓冲区是直接缓冲区还是非直接缓冲区可通过调用其 isDirect()  方法来确定。提供此方法是为了能够在性能关键型代码中执行显式缓冲区管理 。


缓冲区Buffer代码使用
String str = "abcdef";
ByteBuffer b = ByteBuffer.allocate(1024);
System.out.println(b.isDirect());
b.put(str.getBytes());
System.out.println("------------------");
System.out.println("capacity: "+b.capacity());
System.out.println("limit: "+b.limit());
System.out.println("position: "+b.position());
System.out.println("---------flip---------");
b.flip();//切换读取,否则读取不出
System.out.println("capacity: "+b.capacity());
System.out.println("limit: "+b.limit());
System.out.println("position: "+b.position());
System.out.println("---------get---------");
byte[] byt = new byte[b.limit()];
b.get(byt);//获取
System.out.println(""+new String(byt));
System.out.println("capacity: "+b.capacity());
System.out.println("limit: "+b.limit());
System.out.println("position: "+b.position());
System.out.println("---------rewind---------");
b.rewind();//重读
System.out.println("capacity: "+b.capacity());
System.out.println("limit: "+b.limit());
System.out.println("position: "+b.position());
System.out.println("---------clear---------");
b.clear();//回到原始状态,但并没有清空数据,数据处于被遗忘状态
System.out.println("capacity: "+b.capacity());
System.out.println("limit: "+b.limit());
System.out.println("position: "+b.position());
直接缓冲区(内存映射文件方式)代码使用
FileChannel reader = FileChannel.open(Paths.get("D:/c.jpg"), StandardOpenOption.READ);
FileChannel writer = FileChannel.open(Paths.get("D:/c3.jpg"),  StandardOpenOption.WRITE,StandardOpenOption.READ, StandardOpenOption.CREATE);
MappedByteBuffer inMapper = reader.map(MapMode.READ_ONLY, 0, reader.size());
MappedByteBuffer outMapper = writer.map(MapMode.READ_WRITE, 0, reader.size());
byte[] bye = new byte[inMapper.limit()];
inMapper.get(bye);
outMapper.put(bye);
reader.close();
writer.close();
基本正常代码使用
FileChannel inChannel = FileChannel.open(Paths.get("D:/a.avi"), StandardOpenOption.READ);
FileChannel outChannel = FileChannel.open(Paths.get("D:/a1.avi"), StandardOpenOption.CREATE, StandardOpenOption.WRITE);
inChannel.transferTo(0, inChannel.size(), outChannel);
outChannel.close();
inChannel.close();


分散(Scatter)与聚集(Gather)

  • 分散读取:将通道中的数据分散到多个缓冲区中
  • 聚集写入:将多个缓冲区的数据聚集到通道中
按照缓冲区的顺序,从 Channel 中读取的数据依次将 Buffer 填满


                  


                                                     (图一、分散读取)                                                                                                              (图二、聚集写入)                                                  


分散(Scatter)与聚集(Gather)代码
FileChannel reader = input.getChannel();
FileChannel writer = output.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
ByteBuffer buffer1 = ByteBuffer.allocate(2024);
ByteBuffer[] buffers = new ByteBuffer[]{buffer,buffer1};
reader.read(buffers);//分散读取
writer.write(buffers);//聚集写入

评论
或者使用社交账号快捷登录