热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

javanio类_JavaNIO常用归纳

前言:之前的文章《Java文件IO常用归纳》主要写了Java标准IO要注意的细节和技巧,由于网上各种学习途径,所以并没有详细示例等。本文主

前言: 之前的文章《Java文件IO常用归纳》主要写了Java 标准IO要注意的细节和技巧,由于网上各种学习途径,所以并没有详细示例等。本文主要简单看看java的NIO库的用法,并做个小归纳,可以对比标准IO参考一下。

NIO概述

(一)背景

NIO(New IO),在Java 1.4引入的一个新的IO API。【可替代标准IO API】

(二)工作方式

Channels and Buffers(通道和缓冲区):标准的IO基于字节流和字符流进行操作的,而NIO是基于通道(Channel)和缓冲区(Buffer)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。

Asynchronous IO(异步IO):Java NIO可以让你异步的使用IO,例如:当线程从通道读取数据到缓冲区时,线程还是可以进行其他事情。当数据被写入到缓冲区时,线程可以继续处理它。从缓冲区写入通道也类似。

Selectors(选择器):Java NIO引入了选择器的概念,选择器用于监听多个通道的事件(比如:连接打开,数据到达)。因此,单个的线程可以监听多个数据通道

NIO 与 标准IO 的区别

IO面向Stream(流),NIO面向Buffer(缓存)

面向Stream:每次从流中读取一个或多个字节,直到读取所有字节,并没有缓存字节的地方。不能前后移动流中的数据(因为如果要前后移动从流中读取的数据,就需先将其缓存到一个缓存区中)。

面向Buffer【更灵活】:数据读取到一个稍后处理的缓冲区,需要时即可在缓冲区中前后移动(注意:移动前首先需要检查是否该缓冲区中包含你需要处理的数据)。需要确保当更多数据读入缓冲区时,不会覆盖掉区中原有的尚未处理的数据。

IO流都是阻塞的,而NIO有非阻塞模式

IO的流:当一个线程threadA使用IO调用read()/write()操作时,threadA被阻塞,直到一些数据被读取或写入完成,此过程中threadA不能做任何事。

NIO的非阻塞模式:

【非阻塞读】线程threadA从某channel发送请求读取数据时,threadA仅能得到目前可用的数据,若目前没有可用数据,那么threadA不会获取任何数据并可以先做别的事情,而不是保持阻塞,直到有可用数据在这个通道出现。

【非阻塞写】一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。

【应用】线程通常将非阻塞IO的空闲时间用于在其它通道上执行IO操作,所以一个单独的线程现在可以管理多个输入和输出channels。

NIO独有选择器(Selector)

常用API总结

(一)核心接口与类关系图解与分析

FileChannel fileChannel = accessFile.getChannel();

SocketChannel

创建SocketChannel对象:

SocketChannel sc = SocketChannel.open();

设置非阻塞IO状态:

sc.configureBlocking(false);

开始打开连接

sc.connect(new InetSocketAddress("http://jianshu.com", 80));

非阻塞状态下,成功连接前,干别的事

sc.configureBlocking(false);

///...............

while(!sc.finishConnect()){ do other sth.... }

保证非阻塞IO状态下,read()过程不会read 空数据

sc.configureBlocking(false);

///...............

while((int len = sc.read(buf))==0){ do other sth.... }

保证非阻塞IO状态下,write()过程不会write空数据

sc.configureBlocking(false);

///...............

while((int len = sc.write(buf))==0){ do other sth.... }

ServerSocketChannel、DatagramChannel、Selector和Pipe的写法与方法说明,由于篇幅原因,可以参考下文的示例。

(三)文件IO --- FileChannel

读文件

public static byte[] readBytes(String fileName) {

try {

///获取对应文件的FileChannel对象

RandomAccessFile accessFile = new RandomAccessFile(fileName, "rw");

FileChannel fileChannel = accessFile.getChannel();

/// 创建一个缓冲区(大小为48byte)

ByteBuffer byteBuffer = ByteBuffer.allocate(48);

StringBuilder builder = new StringBuilder();

int bytesRead = fileChannel.read(byteBuffer);

while (bytesRead != -1) {

System.out.println("Read " + bytesRead);

///翻转buffer

byteBuffer.flip();

///每次读取完之后,输出缓存中的内容

while (byteBuffer.hasRemaining()) {

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

builder.append((char) byteBuffer.get());

}

///然后清空缓存区

byteBuffer.clear();

///重新再读数据到缓存区中

bytesRead = fileChannel.read(byteBuffer);

}

accessFile.close();

return builder.toString().getBytes();

} catch (IOException e) {

e.printStackTrace();

return null;

}

}

写入文件

public static void writeBytes(String fileName, byte[] data) {

try {

RandomAccessFile accessFile = new RandomAccessFile(fileName, "rw");

FileChannel channel = accessFile.getChannel();

ByteBuffer buffer = ByteBuffer.allocate(48);

buffer.put(data);

channel.write(buffer);

} catch (FileNotFoundException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

}

}

通道间内容传输

/**

* channel 间的传输

*

* @param sFileName 源文件

* @param dFileName 目标文件

*/

public static void channelToChannel(String sFileName, String dFileName) {

try {

RandomAccessFile sAccess = new RandomAccessFile(sFileName, "rw");

RandomAccessFile dAccess = new RandomAccessFile(dFileName, "rw");

FileChannel sChannel = sAccess.getChannel();

FileChannel dChannel = dAccess.getChannel();

long pos = 0;

long sCount = sChannel.size();

long dCount = dChannel.size();

// dChannel.transferFrom(sChannel,pos,sCount);//dChannel 必须是FileChannel

sChannel.transferTo(pos, dCount, dChannel);///sChannel 是FileChannel

} catch (FileNotFoundException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

}

}

(四)TCP通信 --- SocketChannel

基本的C/S TCP通信

Client客户端写法:

/**

* Client SocketChannel 写法:

*/

public static void client(String fileName) {

SocketChannel sc = null;

try {

// 创建一个SocketChannel 通道

TODO: FileChannel无法设置为非阻塞模式,它总是运行在阻塞模式下

sc = SocketChannel.open();

///TODO:非阻塞IO状态下,socketChannel就可以异步地执行read()、write()、connect()方法了

sc.configureBlocking(false);

sc.connect(new InetSocketAddress("http://jianshu.com", 80));

while (!sc.finishConnect()) {///保证在connect成功之前,可以做别的事情

//做点别的事。。。。。

}

while((int len = sc.read(xxx))==0){ ///保证NBIO下,read数据不会read空

// 做别的事。。。

}

while((int len = sc.write(xxx))==0){///保证NBIO下,write数据不会write空

// 做别的事。。。

}

} catch (IOException e) {

e.printStackTrace();

} finally {

try {

if (sc != null) {

sc.close();

}

} catch (IOException e) {

e.printStackTrace();

}

}

}

```

Server服务端写法:

/**

* 关于:ServerSocketChannel

*/

public static void serverSocketChannel() {

ServerSocketChannel serverSocketChannel = null;

try {

///打开

serverSocketChannel = ServerSocketChannel.open();

///连接并开始监听TCP 9999端口

serverSocketChannel.socket().bind(new InetSocketAddress(9999));

///TODO:可设置非阻塞状态(需要检查accept到的socketChannel是否为null)

serverSocketChannel.configureBlocking(false);

while (true) {

SocketChannel socketChannel =

serverSocketChannel.accept();

//TODO: 非阻塞时需要考虑返回的socketChannel对象是否为null

if(socketChannel != null){

//do something with socketChannel...

}

//do something with socketChannel...

}

} catch (IOException e) {

e.printStackTrace();

} finally {

if (serverSocketChannel != null)

try {

serverSocketChannel.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}

```

配合Selector,简化SocketChannel在非阻塞IO状态下的Null情况监测逻辑

/**

* 关于 选择器 和 SocketChannel 的配合使用

*/

public static void selectorAndSocketChannel(String fileName) {

SocketChannel sc1 = null;

SocketChannel sc2 = null;

SocketChannel sc3 = null;

try {

// 创建几个SocketChannel 通道

TODO: FileChannel无法设置为非阻塞模式,它总是运行在阻塞模式下

sc1 = SocketChannel.open();

sc2 = SocketChannel.open();

sc3 = SocketChannel.open();

///TODO:非阻塞IO状态下,socketChannel就可以异步地执行read()、write()、connect()方法了

sc1.configureBlocking(false);

sc2.configureBlocking(false);

sc3.configureBlocking(false);

sc1.connect(new InetSocketAddress("http://jenkov.com", 80));

sc2.connect(new InetSocketAddress("http://jenkov.com", 80));

sc3.connect(new InetSocketAddress("http://jenkov.com", 80));

// 创建Selector

Selector selector = Selector.open();

// 注册channels

SelectionKey key1 = sc1.register(selector, SelectionKey.OP_READ);

SelectionKey key2 = sc2.register(selector, SelectionKey.OP_READ);

SelectionKey key3 = sc3.register(selector, SelectionKey.OP_READ);

// 持续监控selector的四个事件(接受、连接、读、写)是否就绪

while (true) {

int readyChannels = selector.select();

if (readyChannels == 0) continue;

Set selectedKeys = selector.selectedKeys();

Iterator keyIterator = selectedKeys.iterator();

while (keyIterator.hasNext()) {

SelectionKey key = (SelectionKey) keyIterator.next();

if (key.isAcceptable()) {

// a connection was accepted by a ServerSocketChannel.

///我的这个连接请求被服务端接受了

} else if (key.isConnectable()) {

// a connection was established with a remote server.

///已经连接上

} else if (key.isReadable()) {

// a channel is ready for reading

///可读数据

} else if (key.isWritable()) {

// a channel is ready for writing

///可写数据

}

}

keyIterator.remove();

}

} catch (IOException e) {

e.printStackTrace();

} finally {

try {

if (sc1 != null) {

sc1.close();

}

if (sc2 != null) {

sc2.close();

}

if (sc3 != null) {

sc3.close();

}

} catch (IOException e) {

e.printStackTrace();

}

}

}

(五)UDP通信 --- DatagramChannel

收发UDP数据包 的简单示例

/**

* 关于:DatagramChannel

* UDP 无连接网络协议

* 发送和接收的是数据包

*/

public static void datagramChannel() {

DatagramChannel datagramChannel = null;

try {

///打开

datagramChannel = DatagramChannel.open();

///连接并开始监听UDP 9999端口

datagramChannel.socket().bind(new InetSocketAddress(9999));

// 接收数据包(receive()方法会将接收到的数据包内容复制到指定的Buffer. 如果Buffer容不下收到的数据,多出的数据将被丢弃。 )

ByteBuffer buf = ByteBuffer.allocate(48);

buf.clear();

datagramChannel.receive(buf);

// 发送数据 send()

String sendMsg = "要发送的数据";

ByteBuffer sendBuf = ByteBuffer.allocate(48);

sendBuf.clear();

sendBuf.put(sendMsg.getBytes());

sendBuf.flip();

datagramChannel.send(sendBuf,new InetSocketAddress("xxxxx",80));

// TODO: 连接到特定的地址(锁住DatagramChannel ,让其只能从特定地址收发数据 因为UDP无连接,本身没有真正的连接产出)

datagramChannel.connect(new InetSocketAddress("jenkov.com", 80));

///连接后,也可以使用Channal 的read()和write()方法,就像在用传统的通道一样。只是在数据传送方面没有任何保证

} catch (IOException e) {

e.printStackTrace();

} finally {

if (datagramChannel != null)

try {

datagramChannel.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}

(六)NIO管道(Pipe)

首先,什么是NIO管道,下图可以看出其内部结构和功能特点:

NIO Pipe,是两个线程之间的单向连接通道(读下图可知)

Pipe类内部有两个成员属性,分别是:

Pipe.SinkChannel:数据入口通道

Pipe.SourceChannel:数据出口通道

整体原理:ThreadA中获取的数据通过SinkChannel传入(写入)管道,当ThreadB要读取ThreadA的数据,则通过管道的SourceChannel传出(读取)数据。

093b7c408dba

NIO Pipe原理图解

示例: 管道传输数据

/**

* 关于NIO管道(Pipe)

* 定义:2个线程之间的单向数据连接

*/

public static void aboutPipe(){

Pipe pipe=null;

try {

/// 打开管道

pipe = Pipe.open();

///TODO: 一、 向管道写入数据

/// 访问Pipe.sinkChannel,向Pipe写入数据

/// 首先,获取Pipe.sinkChannel

Pipe.SinkChannel sinkChannel = pipe.sink();

/// 然后,调用write(),开始写入数据

String newData = "New String to write to file..." + System.currentTimeMillis();

ByteBuffer buf = ByteBuffer.allocate(48);

buf.clear();

buf.put(newData.getBytes());

buf.flip();

while(buf.hasRemaining()){

sinkChannel.write(buf);

}

// TODO: 二、读取管道中的数据

// 首先,获取Pipe.sourceChannel

Pipe.SourceChannel sourceChannel = pipe.source();

/// 读取数据到buffer

ByteBuffer buf2 = ByteBuffer.allocate(48);

int bytesRead = sourceChannel.read(buf2);

} catch (IOException e) {

e.printStackTrace();

}

}

附录:java.nio包相关接口与类图一览

093b7c408dba

Buffer类图

093b7c408dba

java.nio.channels 相关接口关系图

093b7c408dba

java.nio.channels 相关类图

093b7c408dba

java.nio.charset 相关类图(这个包主要做不同编码格式的加解密等工作)

参考文章



推荐阅读
  • 本文介绍了Swing组件的用法,重点讲解了图标接口的定义和创建方法。图标接口用来将图标与各种组件相关联,可以是简单的绘画或使用磁盘上的GIF格式图像。文章详细介绍了图标接口的属性和绘制方法,并给出了一个菱形图标的实现示例。该示例可以配置图标的尺寸、颜色和填充状态。 ... [详细]
  • Android工程师面试准备及设计模式使用场景
    本文介绍了Android工程师面试准备的经验,包括面试流程和重点准备内容。同时,还介绍了建造者模式的使用场景,以及在Android开发中的具体应用。 ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • 本文介绍了Java工具类库Hutool,该工具包封装了对文件、流、加密解密、转码、正则、线程、XML等JDK方法的封装,并提供了各种Util工具类。同时,还介绍了Hutool的组件,包括动态代理、布隆过滤、缓存、定时任务等功能。该工具包可以简化Java代码,提高开发效率。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 图解redis的持久化存储机制RDB和AOF的原理和优缺点
    本文通过图解的方式介绍了redis的持久化存储机制RDB和AOF的原理和优缺点。RDB是将redis内存中的数据保存为快照文件,恢复速度较快但不支持拉链式快照。AOF是将操作日志保存到磁盘,实时存储数据但恢复速度较慢。文章详细分析了两种机制的优缺点,帮助读者更好地理解redis的持久化存储策略。 ... [详细]
  • 基于事件驱动的并发编程及其消息通信机制的同步与异步、阻塞与非阻塞、IO模型的分类
    本文介绍了基于事件驱动的并发编程中的消息通信机制,包括同步和异步的概念及其区别,阻塞和非阻塞的状态,以及IO模型的分类。同步阻塞IO、同步非阻塞IO、异步阻塞IO和异步非阻塞IO等不同的IO模型被详细解释。这些概念和模型对于理解并发编程中的消息通信和IO操作具有重要意义。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • 本文介绍了操作系统的定义和功能,包括操作系统的本质、用户界面以及系统调用的分类。同时还介绍了进程和线程的区别,包括进程和线程的定义和作用。 ... [详细]
  • 本文介绍了Android中的assets目录和raw目录的共同点和区别,包括获取资源的方法、目录结构的限制以及列出资源的能力。同时,还解释了raw目录中资源文件生成的ID,并说明了这些目录的使用方法。 ... [详细]
  • 本文介绍了OkHttp3的基本使用和特性,包括支持HTTP/2、连接池、GZIP压缩、缓存等功能。同时还提到了OkHttp3的适用平台和源码阅读计划。文章还介绍了OkHttp3的请求/响应API的设计和使用方式,包括阻塞式的同步请求和带回调的异步请求。 ... [详细]
author-avatar
songaihua853185820299
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有