JAVA语言NIO基础使用教程
小标 2018-11-05 来源 : 阅读 1291 评论 0

摘要:本文主要向大家介绍了JAVA语言NIO基础使用教程,通过具体的内容向大家展示,希望对大家学习JAVA语言有所帮助。

本文主要向大家介绍了JAVA语言NIO基础使用教程,通过具体的内容向大家展示,希望对大家学习JAVA语言有所帮助。


前言


Java中的输入输出操作主要针对数据文件和Socket对象,传统的Java IO操作都是阻塞进行的,比如在读取网络数据的时候如果数据还没有返回那么read方法就会被阻塞一直等到网络数据返回或者发生错误,再读取的时候也采用流式读取数据。在Java1.4中引入了NIO处理输入输出,NIO采用内存映射文件的方式将文件或文件的一段映射到内存中,同时它使用块IO来处理数据效率更高。


缓冲区


缓冲区对象其实就是容器对象,在原来的IO操作中所有的数据读写都是直接通过Stream对象来完成,在NIO中读取数据需要放到缓冲区中,写入数据也需要放到缓冲区中。Java中所有的基础类型都有对应的缓冲区对象,ByteBuffer、DoubleBuffer、IntBuffer等。


缓冲区本质上来说是数组对象,它有三个重要的属性position、limit和capacity,它们的值有(0 <= position <= limit <= capacity)的大小关系。其中position作为操作的开始位置,不管是读取get操作还是写入put操作,position都会比自动更新;limit表明操作position时最多能够操作到的位置,capacity代表当前的缓冲区大小值。


public static void main(String[] args) {

 IntBuffer intBuffer = IntBuffer.allocate(10);

 for (int i = 0; i < intBuffer.capacity() - 3; i++) {

  intBuffer.put(i * 5);

 }

 

 intBuffer.flip();

 while (intBuffer.hasRemaining()) {

  System.out.print(intBuffer.get() + " ");

 }

}

// 0 5 10 15 20 25 30

   


上面的示例中IntBuffer由于是抽象类无法被实例化,就使用IntBuffer提供的静态方法初始化生成子类HeapIntBuffer对象,初始化它的容量为10个Int值,接下来在intBuffer对象中防止7个整数值,这时position=7,limit=10,后面通过flip调用将position=0,limit=7,hasRemaining方法不断比较position和limit属性大小,get方法会增加position大小,当position=limit代表数据完全读取完毕。


缓冲区的数据作为数组存放在JVM堆内存中,不过不同的JVM中数组对象的储存格式有所不同,为此引入了直接缓冲区,直接缓冲区位于JVM堆内存之外,它的请求和释放不参与GC,直接缓冲区使用的内存是通过调用本地操作系统的代码分配的,绕过了标准JVM堆栈。不过通过下面的Demo测试发现分配的内存被算到了堆内存容量中。


public static void main(String[] args) {

 // 直接分配10M内存

 ByteBuffer byteBuffer = ByteBuffer.allocateDirect(10 * 1024 * 1024);

 System.out.println("Allocate Successfully!");

 try {

  Thread.sleep(3000);

 } catch (InterruptedException e) {

  e.printStackTrace();

 }

}

 

java -Xms5m -Xmx5m NewIOTest

Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory

  at java.nio.Bits.reserveMemory(Bits.java:693)

  at java.nio.DirectByteBuffer.<init>(DirectByteBuffer.java:123)

  at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:311)

  at NewIOTest.main(NewIOTest.java:5)

 

java -Xms15m -Xmx15m NewIOTest

Allocate Successfully!</init>

   


通道


通道类似旧IO操作中的Stream对象,不过通道对象能够读也能够写,通道只能读取缓冲区中的数据获取将数据写入到缓冲区中。通道对象代表的是数据文件或者Socket等与程序之间的连接通路,因而所有的Channel都不是通过构造器创建的,而是通过传统的节点InputStream、OutputStream的getChannel方法来返回响应的Channel。


FileInputStream fis = new FileInputStream("Main.java");

FileChannel fileChannel = fis.getChannel();

ByteBuffer byteBuffer = ByteBuffer.allocate(2048);

fileChannel.read(byteBuffer);

byteBuffer.flip();

while (byteBuffer.hasRemaining()) {

 System.out.print((char) byteBuffer.get());

}

   


选择子


Selector能够检测多个注册的通道上是否有事件发生,如果有事件发生,便获取事件然后针对每个事件进行相应的响应处理。这样一来,只是用一个单线程就可以管理多个通道,也就是管理多个连接。这样使得只有在连接真正有读写事件发生时,才会调用函数来进行读写,就大大地减少了系统开销,并且不必为每个连接都创建一个线程,不用去维护多个线程,并且避免了多线程之间的上下文切换导致的开销。这里通过实现一个简单的服务器只需要每次将用户输入的内容添加“Hello”之后返回到客户端。


public static void main(String[] args) throws Exception {

 ServerSocketChannel socketChannel = ServerSocketChannel.open();

 // 设置Socket是非阻塞的

 socketChannel.configureBlocking(false);

 // 绑定到5555端口

 socketChannel.bind(new InetSocketAddress(5555));

 Selector selector = Selector.open();

 // 为服务器注册接收事件

 socketChannel.register(selector, SelectionKey.OP_ACCEPT);

 

 while (true) {

  int count = selector.select();

  if (count <= 0) continue;

  Set<selectionkey> selectionKeySet = selector.selectedKeys();

  Iterator<selectionkey> iterable = selectionKeySet.iterator();

  while (iterable.hasNext()) {

SelectionKey key = iterable.next();

if (key.isAcceptable()) {

 // 如果accept被激活,说明有客户端连接

 SocketChannel newSocketChannel = socketChannel.accept();

 newSocketChannel.configureBlocking(false);

 // 为客户端连接添注册读事件

 newSocketChannel.register(selector, SelectionKey.OP_READ);

} else if (key.isReadable()) {

 // 如果读到客户端的数据

 SocketChannel clientSocket = (SocketChannel) key.channel();

 ByteBuffer byteBuffer = ByteBuffer.allocate(1024);

 clientSocket.read(byteBuffer);

 System.out.println(new String(byteBuffer.array()));

 String text = "Hello: " + new String(byteBuffer.array())  + "\r\n";

 // 将Hello + 客户端数据返回给用户

 clientSocket.write(ByteBuffer.wrap(text.getBytes()));

}

iterable.remove();

  }

 }

}</selectionkey></selectionkey>

   


          

本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注编程语言JAVA频道!


本文由 @小标 发布于职坐标。未经许可,禁止转载。
喜欢 | 1 不喜欢 | 0
看完这篇文章有何感觉?已经有1人表态,100%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式AI+学习就业服务平台 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved