当前位置: 首页 > news >正文

Java NIO2:NIO概述

一、概述

     从JDK1.4开始,Java提供了一系列改进的输入/输出处理的新特性,被统称为NIO(即New I/O)。新增了许多用于处理输入输出的类,这些类都被放在java.nio包及子包下,并且对原java.io包中的很多类进行改写,新增了满足NIO的功能。NIO采用内存映射文件的方式来处理输入输出,NIO将文件或文件的一段区域映射到内存中,这样就可以像访问内存一样访问文件了。

NIO 与原来的 I/O 有同样的作用和目的,但是它使用不同的方式?  块I/O。块 I/O 的效率可以比流 I/O 高许多。

流与块的比较

     原来的 I/O 库(在 java.io.*中) 与 NIO 最重要的区别是数据打包和传输的方式。正如前面提到的,原来的 I/O 以流的方式处理数据,而 NIO 以块的方式处理数据。

     面向流 的 I/O 系统一次一个字节地处理数据。一个输入流产生一个字节的数据,一个输出流消费一个字节的数据。为流式数据创建过滤器非常容易。链接几个过滤器,以便每个过滤器只负责单个复杂处理机制的一部分,这样也是相对简单的。不利的一面是,面向流的 I/O 通常相当慢。

     一个 面向块 的 I/O 系统以块的形式处理数据。每一个操作都在一步中产生或者消费一个数据块。按块处理数据比按(流式的)字节处理数据要快得多。但是面向块的 I/O 缺少一些面向流的 I/O 所具有的优雅性和简单性。

     在NIO中有几个核心对象需要掌握:缓冲区(Buffer)、通道(Channel)、选择器(Selector)。

二、缓冲区Buffer

     缓冲区实际上是一个容器对象,更直接的说,其实就是一个数组,在NIO库中,所有数据都是用缓冲区处理的。在读取数据时,它是直接读到缓冲区中的; 在写入数据时,它也是写入到缓冲区中的;任何时候访问 NIO 中的数据,都是将它放到缓冲区中。而在面向流I/O系统中,所有数据都是直接写入或者直接将数据读取到Stream对象中。具体看下面这张图就理解了:

    上面的图描述了从一个客户端向服务端发送数据,然后服务端接收数据的过程。客户端发送数据时,必须先将数据存入Buffer中,然后将Buffer中的内容写入通道。服务端这边接收数据必须通过Channel将数据读入到Buffer中,然后再从Buffer中取出数据来处理。

    在NIO中,所有的缓冲区类型都继承于抽象类Buffer,最常用的就是ByteBuffer,对于Java中的基本类型,基本都有一个具体Buffer类型与之相对应,它们之间的继承关系如下图所示:

下面是一个简单使用IntBuffer的例子:

package com.demo.nio;

import java.nio.IntBuffer;

public class TestIntBuffer {

    public static void main(String[] args) {
        // 分配新的int缓冲区,参数为缓冲区容量
        // 新缓冲区的当前位置将为零,其界限(限制位置)将为其容量。它将具有一个底层实现数组,其数组偏移量将为零。
        IntBuffer buffer = IntBuffer.allocate(8);

        for (int i = 0; i < buffer.capacity(); ++i) {
            int j = 2 * (i + 1);
            // 将给定整数写入此缓冲区的当前位置,当前位置递增
            buffer.put(j);
        }

        // 重设此缓冲区,将限制设置为当前位置,然后将当前位置设置为0
        buffer.flip();

        // 查看在当前位置和限制位置之间是否有元素
        while (buffer.hasRemaining()) {
            // 读取此缓冲区当前位置的整数,然后当前位置递增
            int j = buffer.get();
            System.out.print(j + "  ");
        }
    }
}

运行后可以看到:

三、通道Channel

      Channel和传统IO中的Stream很相似。虽然很相似,但是有很大的区别,主要区别为:通道是双向的,通过一个Channel既可以进行读,也可以进行写;而Stream只能进行单向操作,通过一个Stream只能进行读或者写,比如InputStream只能进行读取操作,OutputStream只能进行写操作;

      通道是一个对象,通过它可以读取和写入数据,当然了所有数据都通过Buffer对象来处理。我们永远不会将字节直接写入通道中,相反是将数据写入包含一个或者多个字节的缓冲区。同样不会直接从通道中读取字节,而是将数据从通道读入缓冲区,再从缓冲区获取这个字节。

      在NIO中,提供了多种通道对象,而所有的通道对象都实现了Channel接口。它们之间的继承关系如下图所示:

 

    Channel(通道)表示到实体如硬件设备、文件、网络套接字或可以执行一个或多个不同I/O操作的程序组件的开放的连接。所有的Channel都不是通过构造器创建的,而是通过传统的节点InputStream、OutputStream的getChannel方法来返回响应的Channel。

  Channel中最常用的三个类方法就是map、read和write,其中map方法用于将Channel对应的部分或全部数据映射成ByteBuffer,而read或write方法有一系列的重载形式,这些方法用于从Buffer中读取数据或向Buffer中写入数据。

1、使用NIO读取数据

在前面我们说过,任何时候读取数据,都不是直接从通道读取,而是从通道读取到缓冲区。所以使用NIO读取数据可以分为下面三个步骤: 
(1). 从FileInputStream获取Channel 
(2). 创建Buffer 
(3). 将数据从Channel读取到Buffer中

下面是一个简单的使用NIO从文件中读取数据的例子:

import java.io.*;  
import java.nio.*;  
import java.nio.channels.*;  
  
public class Program {  
    public static void main( String args[] ) throws Exception {  
        FileInputStream fin = new FileInputStream("c:\\test.txt");  
          
        // 获取通道  
        FileChannel fc = fin.getChannel();  
          
        // 创建缓冲区  
        ByteBuffer buffer = ByteBuffer.allocate(1024);  
          
        // 读取数据到缓冲区  
        fc.read(buffer);  
          
        buffer.flip();  
          
        while (buffer.remaining()>0) {  
            byte b = buffer.get();  
            System.out.print(((char)b));  
        }  
          
        fin.close();  
    }  
}  

2、使用NIO写入数据

使用NIO写入数据与读取数据的过程类似,同样数据不是直接写入通道,而是写入缓冲区,可以分为下面三个步骤:
(1). 从FileInputStream获取Channel
(2). 创建Buffer
(3). 将数据从Channel写入到Buffer中

下面是一个简单的使用NIO向文件中写入数据的例子:

package com.demo.nio;

import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class Program {
    
    private static final byte message[] = { 83, 111, 109, 101, 32,
        98, 121, 116, 101, 115, 46 };

    public static void main( String args[] ) throws Exception {
        FileOutputStream fout = new FileOutputStream( "c:\\test.txt" );
        
        FileChannel fc = fout.getChannel();
        
        ByteBuffer buffer = ByteBuffer.allocate( 1024 );
        
        for (int i=0; i<message.length; ++i) {
            buffer.put( message[i] );
        }
        
        buffer.flip();
        
        fc.write( buffer );
        
        fout.close();
    }

}

四、选择器Selector

      Selector类是NIO的核心类,Selector能够检测多个注册的通道上是否有事件发生,如果有事件发生,便获取事件然后针对每个事件进行相应的响应处理。这样一来,只是用一个单线程就可以管理多个通道,也就是管理多个连接。这样使得只有在连接真正有读写事件发生时,才会调用函数来进行读写,就大大地减少了系统开销,并且不必为每个连接都创建一个线程,不用去维护多个线程,并且避免了多线程之间的上下文切换导致的开销。

  与Selector有关的一个关键类是SelectionKey,一个SelectionKey表示一个到达的事件,这2个类构成了服务端处理业务的关键逻辑。

相关文章:

  • P3项目--XML文件解析 笔记
  • 最强DE 战斗力 (nyoj 541)
  • MaxCompute数据安全机制
  • Spring配置中transactionAttributes的使用方法和作用
  • BZOJ 1874: [BeiJing2009 WinterCamp]取石子游戏 [Nim游戏 SG函数]
  • 编程之美2015资格赛 题目2 : 回文字符序列 [ 区间dp ]
  • StackExchange.Redis加载Lua脚本进行模糊查询的批量删除和修改
  • Java培训小结
  • Flume NG 学习笔记(六)Selector(复用与复制)测试
  • [开源]用MQL4实现MD5加密
  • 清除浮动
  • zabbix监控mysql主从状态
  • 大整数算法[12] 有符号乘法
  • 多线程(七)---多线程同步相关问题
  • java基础入门1到100的奇数求和
  • Fastjson的基本使用方法大全
  • Mybatis初体验
  • PhantomJS 安装
  • Spark in action on Kubernetes - Playground搭建与架构浅析
  • Spark学习笔记之相关记录
  • Work@Alibaba 阿里巴巴的企业应用构建之路
  • 持续集成与持续部署宝典Part 2:创建持续集成流水线
  • 从零开始的webpack生活-0x009:FilesLoader装载文件
  • 大主子表关联的性能优化方法
  • 分类模型——Logistics Regression
  • 前端之React实战:创建跨平台的项目架构
  • 浅谈web中前端模板引擎的使用
  • 使用权重正则化较少模型过拟合
  • 通过git安装npm私有模块
  • 问题之ssh中Host key verification failed的解决
  • 限制Java线程池运行线程以及等待线程数量的策略
  • 协程
  • ​TypeScript都不会用,也敢说会前端?
  • #使用清华镜像源 安装/更新 指定版本tensorflow
  • #我与Java虚拟机的故事#连载09:面试大厂逃不过的JVM
  • (02)Hive SQL编译成MapReduce任务的过程
  • (1)(1.8) MSP(MultiWii 串行协议)(4.1 版)
  • (33)STM32——485实验笔记
  • (Java岗)秋招打卡!一本学历拿下美团、阿里、快手、米哈游offer
  • (vue)el-checkbox 实现展示区分 label 和 value(展示值与选中获取值需不同)
  • (二)pulsar安装在独立的docker中,python测试
  • (二)七种元启发算法(DBO、LO、SWO、COA、LSO、KOA、GRO)求解无人机路径规划MATLAB
  • (论文阅读26/100)Weakly-supervised learning with convolutional neural networks
  • (算法)Travel Information Center
  • (一)Thymeleaf用法——Thymeleaf简介
  • (转)linux 命令大全
  • (转)拼包函数及网络封包的异常处理(含代码)
  • .axf 转化 .bin文件 的方法
  • .desktop 桌面快捷_Linux桌面环境那么多,这几款优秀的任你选
  • .NET Core IdentityServer4实战-开篇介绍与规划
  • .net wcf memory gates checking failed
  • .NET 简介:跨平台、开源、高性能的开发平台
  • .NET 使用 XPath 来读写 XML 文件
  • .NET/C# 在代码中测量代码执行耗时的建议(比较系统性能计数器和系统时间)
  • .net下的富文本编辑器FCKeditor的配置方法