文档章节

【转】java NiO 学习笔记

小灰灰Blog
 小灰灰Blog
发布于 2017/05/19 18:20
字数 1716
阅读 29
收藏 0
点赞 0
评论 0

1. Channel

NIO中常用的通道有四个:

  • FileChannel : 文件
  • DatagramChannel : UDP 读取网络数据
  • SocketChannel : TCP 读取网络数据
  • ServerSocketChannel : 监听新进来的TCP连接,像Web服务器那样。对每一个新进来的连接都会创建一个SocketChannel

Java NIO的通道类似流,但又有些不同:

  • 既可以从通道中读取数据,又可以写数据到通道。但流的读写通常是单向的。

  • 通道可以异步地读写。

  • 通道中的数据总是要先读到一个Buffer,或者总是要从一个Buffer中写入。

    http://ifeve.com/wp-content/uploads/2013/06/overview-channels-buffers.png

2. Buffer

和Channel进行交互,从Channel中读取数据,or将数据写入Channel

基本用法

当向buffer写入数据时,buffer会记录下写了多少数据。一旦要读取数据,需要通过flip()方法将Buffer从写模式切换到读模式。在读模式下,可以读取之前写入到buffer的所有数据

一旦读完了所有的数据,就需要清空缓冲区,让它可以再次被写入

读写数据的一般步骤

  • 写入数据到Buffer: ByteBuffer buf = ByteBuffer.allocate(48); int bytesRead =channel.read(buf)
  • 调用flip()方法: buf.flip()
  • 从Buffer中读取数据 : buf.get()
  • 清空缓存区: clear()方法会清空整个缓冲区。compact()方法只会清除已经读过的数据
RandomAccessFile aFile = new RandomAccessFile("data/nio-data.txt", "rw");
FileChannel inChannel = aFile.getChannel();

//create buffer with capacity of 48 bytes
ByteBuffer buf = ByteBuffer.allocate(48);

int bytesRead = inChannel.read(buf); //read into buffer.
while (bytesRead != -1) {

  buf.flip();  //make buffer ready for read

  while(buf.hasRemaining()){
      System.out.print((char) buf.get()); // read 1 byte at a time
  }

  buf.clear(); //make buffer ready for writing
  bytesRead = inChannel.read(buf);
}
aFile.close();

三个参数 capacity,position和limit

http://ifeve.com/wp-content/uploads/2013/06/buffers-modes.png

capacity

表示容量

position

表示当前读写的位置,读模式时,从该位置开始读数据,读完之后,移到下一个位置;写模式时,从该位置写入数据,写完之后,移到下一个可写的位置;当有写切换到读时,重置为0

limit

限制,读模式下,表示最多可以读多少数据;在写模式下,Buffer的limit表示你最多能往Buffer里写多少数据。 写模式下,limit等于Buffer的capacity

分配buffer

每一个Buffer类都有一个allocate方法

ByteBuffer buf = ByteBuffer.allocate(48);

CharBuffer buf = CharBuffer.allocate(1024);

向Buffer中写数据

  1. 从Channel写到Buffer。

    int bytesRead = inChannel.read(buf);

  2. 通过Buffer的put()方法写到Buffer里。

    buf.put(127);

模式切换 flip()方法

flip方法将Buffer从写模式切换到读模式。调用flip()方法会将position设回0,并将limit设置成之前position的值

从Buffer中读取数据

  1. 从Buffer读取数据到Channel

    int bytesWritten = inChannel.write(buf);

  2. 使用get()方法从Buffer中读取数据。

    byte aByte = buf.get()

rewind()方法

Buffer.rewind()将position设回0,所以你可以重读Buffer中的所有数据。limit保持不变,仍然表示能从Buffer中读取多少个元素(byte、char等)

buf.rewind()

3.Scatter/Gather

scatter/gather用于描述从Channel中读取或者写入到Channel的操作

分散(scatter)从Channel中读取是指在读操作时将读取的数据写入多个buffer中。因此,Channel将从Channel中读取的数据“分散(scatter)”到多个Buffer中。

聚集(gather)写入Channel是指在写操作时将多个buffer的数据写入同一个Channel,因此,Channel 将多个Buffer中的数据“聚集(gather)”后发送到Channel。

Scattering Reads

从Channel中读取数据到Buffer

http://ifeve.com/wp-content/uploads/2013/06/scatter.png

使用方法,channel,读取数据到一个 Buffer数组中,填充满一个之后再填充下一个

ByteBuffer header = ByteBuffer.allocate(128);
ByteBuffer body   = ByteBuffer.allocate(1024);

ByteBuffer[] bufferArray = { header, body };

channel.read(bufferArray);

Gathering Writes

将多个Buffer中的数据写入Channel

http://ifeve.com/wp-content/uploads/2013/06/gather.png

ByteBuffer header = ByteBuffer.allocate(128);
ByteBuffer body   = ByteBuffer.allocate(1024);

//write data into buffers

ByteBuffer[] bufferArray = { header, body };

channel.write(bufferArray);

依次将数组中的数据写入Channel,一个写完再写如下一个(注意写入的是有效数据)

4. Selector

Selector(选择器)是Java NIO中能够检测一到多个NIO通道,并能够知晓通道是否为诸如读写事件做好准备的组件。这样,一个单独的线程可以管理多个channel,从而管理多个网络连接

创建

Selector selector = Selector.open();

通道注册

channel.configureBlocking(false);
SelectionKey key = channel.register(selector, Selectionkey.OP_READ | SelectionKey.OP_WRITE);
	```
	
与Selector一起使用时,Channel必须处于非阻塞模式下。这意味着不能将FileChannel与Selector一起使用,因为FileChannel不能切换到非阻塞模式。而套接字通道都可以

register()方法的第二个参数。这是一个“interest集合”,意思是在通过Selector监听Channel时对什么事件感兴趣。可以监听四种不同类型的事件:

- Connect : SelectionKey.OP_CONNECT
- Accept : SelectionKey.OP_ACCEPT
- Read : SelectionKey.OP_READ
- Write : SelectionKey.OP_WRITE


### SelectionKey

通道注册之后,返回一个 `SelectionKey` 对象,其中包含一下属性:

- interest集合
- interest集合是你所选择的感兴趣的事件集合
- 判断是否为Accept事件 : `boolean isInterestedInAccept  = (selectionKey.interestOps() & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT;`

- ready集合
- 通道已经准备就绪的操作的集合 `int readySet = selectionKey.readyOps()`
- 检测channel中什么事件或操作已经就绪: `boolean state = selectionKey.isAcceptable();`

- Channel
- 获取Channel : `Channel  channel  = selectionKey.channel();`

- Selector
- 获取Selector : `Selector selector = selectionKey.selector();`

- 附加的对象(可选)
- 可以将一个对象或者更多信息附着到SelectionKey上,这样就能方便的识别某个给定的通道
- 可以附加 与通道一起使用的Buffer,或是包含聚集数据的某个对象

  selectionKey.attach(theObject);
  Object attachedObj = selectionKey.attachment();

### 通过Selector选择通道
> 一旦向Selector注册了一或多个通道,就可以调用几个重载的select()方法。这些方法返回你所感兴趣的事件(如连接、接受、读或写)已经准备就绪的那些通道


调用了select()方法,并且返回值表明有一个或更多个通道就绪了,然后可以通过调用selector的selectedKeys()方法,访问“已选择键集(selected key set)”中的就绪通道

注意每次迭代末尾的keyIterator.remove()调用。Selector不会自己从已选择键集中移除SelectionKey实例。必须在处理完通道时自己移除。下次该通道变成就绪时,Selector会再次将其放入已选择键集中。

```java
Selector selector = Selector.open();
channel.configureBlocking(false);
SelectionKey key = channel.register(selector, SelectionKey.OP_READ);
while(true) {
int readyChannels = selector.select();
if(readyChannels == 0) continue;
Set selectedKeys = selector.selectedKeys();
Iterator keyIterator = selectedKeys.iterator();
while(keyIterator.hasNext()) {
  SelectionKey key = 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();
}
}

5. FileChannel

文件通道,用于读写文件,阻塞模式

打开FileChannel

需要通过使用一个InputStream、OutputStream或RandomAccessFile来获取一个FileChannel实例

通过 FileInputStream 获取 FileChannel 示意

FileInputStream fileInputStream = new FileInputStream(new File("/tmp/tags.log"));

FileChannel fileChannel = (fileInputStream).getChannel();

ByteBuffer buffer = ByteBuffer.allocate(128);
int size = fileChannel.read(buffer);

buffer.flip();
while (buffer.hasRemaining()) {
    System.out.print(buffer.getChar());
}
fileInputStream.close();

使用 RandomAccessFile 获取 FileChannel 示意

RandomAccessFile randomAccessFile = new RandomAccessFile("/tmp/tags.log", "rw");
FileChannel fileChannel = randomAccessFile.getChannel();

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

ByteBuffer buf = ByteBuffer.allocate(100);
buf.clear();
buf.put(newData.getBytes());

buf.flip();

while(buf.hasRemaining()) {
    fileChannel.write(buf);
}
// 强制写入文件
fileChannel.force(true);



ByteBuffer buffer = ByteBuffer.allocate(100);
int size = fileChannel.read(buffer);

buffer.flip();

while (buffer.hasRemaining()) {
    byte[] bytes = new byte[buffer.limit()];
    buffer.get(bytes, 0, buffer.limit());
    System.out.print(new String(bytes));
}


fileChannel.close();
randomAccessFile.close();

© 著作权归作者所有

共有 人打赏支持
小灰灰Blog
粉丝 160
博文 149
码字总数 257618
作品 0
武汉
程序员
java基础io流——配角也风流(不求甚解)

本章简单介绍几个常见的io流派生。 1:数据操作流(操作基本类型数据的流)(理解) 代码示例: 2:内存操作流(理解) 用于处理临时存储信息的,程序结束,数据就从内存中消失。 代码示例: 注:查看...

潇潇漓燃 ⋅ 05/30 ⋅ 0

Jenkins 教程(一)实现自动化打包及邮件通知

个人不喜欢装腔作势一堆专业术语放上去,让大多数人看不懂来提升逼格(所谓的专家),所以我简单的介绍jenkins是干啥的。本文使用jenkins,就是让它把git仓库里的东西取出来,然后在jenkins容器...

FantJ ⋅ 05/26 ⋅ 0

为什么大批的JAVA程序员都是在转大数据

前言 首先JAVA的精密,强大,拥有其它语言不可替代的性能和可维护性,早已经是成为最受欢迎的编程语言之一,很多人想进入IT行业,首选的第一门语言就是JAVA。但是,在未来10年肯定是大数据的...

JAVA丶学习 ⋅ 04/18 ⋅ 0

golang自定义路由控制实现(一)

    由于本人之前一直是Java Coder,在Java web开发中其实大家都很依赖框架,所以当在学习Golang的时候,自己便想着在Go开发中脱离框架,自己动手造框架来练习。通过学习借鉴Java的思想还...

1Day ⋅ 05/11 ⋅ 0

Java NIO之Selector(选择器)

历史回顾: Java NIO 概览 Java NIO 之 Buffer(缓冲区) Java NIO 之 Channel(通道) 其他高赞文章: 面试中关于Redis的问题看这篇就够了 一文轻松搞懂redis集群原理及搭建与使用 超详细的Jav...

山川_84b6 ⋅ 05/16 ⋅ 0

Java 5 、6、 7中新特性

JDK5新特性(与1.4相比)【转】 1 循环 for (type variable : array){ body} for (type variable : arrayList){body} 而1.4必须是: for (int i = 0; i < array.length; i++){ type variabl......

thinkyoung ⋅ 2014/10/14 ⋅ 0

Java程序员必读书单,家族又添新成员

点击关注异步图书,置顶公众号 每天与你分享IT好书 技术干货 职场知识 参与文末话题讨论,每日赠送异步图书。 ——异步小编 有些革命出其不意地吸引了全世界的眼球。Twitter、Linux操作系统和...

异步社区 ⋅ 05/09 ⋅ 0

Java NIO 系列教程 -- delete

(一) Java NIO 概述 Java NIO 由以下几个核心部分组成: Channels Buffers Selectors 虽然Java NIO 中除此之外还有很多类和组件,但在我看来,Channel,Buffer 和 Selector 构成了核心的A...

数据之美 ⋅ 2013/06/09 ⋅ 4

学习Java和Spring Boot Cloud ,不妨看看这个

专注于编程、互联网动态。最终将总结的技术、心得、经验(数据结构与算法、源码分析等)分享给大家,这里不只限于技术!还有职场心得、生活感悟、以及面经。 1 java版web项目 java版web项目,...

b644rofp20z37485o35m ⋅ 05/04 ⋅ 0

2018年Java编程学习面试最全知识点总结

Java是一种可以撰写跨平台应用软件的面向对象的程序设计语言。Java 技术具有卓越的通用性、高效性、平台移植性和安全性,广泛应用于PC、数据中心、游戏控制台、科学超级计算机、移动电话和互...

Java小辰 ⋅ 05/14 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

到底会改名吗?微软GVFS 改名之争

微软去年透露了 Git Virtual File System(GVFS)项目,GVFS 是 Git 版本控制系统的一个开源插件,允许 Git 处理 TB 规模的代码库,比如 270 GB 的 Windows 代码库。该项目公布之初就引发了争...

linux-tao ⋅ 30分钟前 ⋅ 0

笔试题之Java基础部分【简】【二】

1.静态变量和实例变量的区别 在语法定义上的区别:静态变量前要加static关键字,而实例变量前则不加。在程序运行时的区别:实例变量属于某个对象的属性,必须创建了实例对象,其中的实例变...

anlve ⋅ 47分钟前 ⋅ 0

Lombok简单介绍及使用

官网 通过简单注解来精简代码达到消除冗长代码的目的 优点 提高编程效率 使代码更简洁 消除冗长代码 避免修改字段名字时忘记修改方法名 4.idea中安装lombnok pom.xml引入 <dependency> <grou...

to_ln ⋅ 今天 ⋅ 0

【转】JS浮点数运算Bug的解决办法

37.5*5.5=206.08 (JS算出来是这样的一个结果,我四舍五入取两位小数) 我先怀疑是四舍五入的问题,就直接用JS算了一个结果为:206.08499999999998 怎么会这样,两个只有一位小数的数字相乘,怎...

NickSoki ⋅ 今天 ⋅ 0

table eg

user_id user_name full_name 1 zhangsan 张三 2 lisi 李四 `` ™ [========] 2018-06-18 09:42:06 星期一½ gdsgagagagdsgasgagadsgdasgagsa...

qwfys ⋅ 今天 ⋅ 0

一个有趣的Java问题

先来看看源码: public class TestDemo { public static void main(String[] args) { Integer a = 10; Integer b = 20; swap(a, b); System.out......

linxyz ⋅ 今天 ⋅ 0

十五周二次课

十五周二次课 17.1mysql主从介绍 17.2准备工作 17.3配置主 17.4配置从 17.5测试主从同步 17.1mysql主从介绍 MySQL主从介绍 MySQL主从又叫做Replication、AB复制。简单讲就是A和B两台机器做主...

河图再现 ⋅ 今天 ⋅ 0

docker安装snmp rrdtool环境

以Ubuntu16:04作为基础版本 docker pull ubuntu:16.04 启动一个容器 docker run -d -i -t --name flow_mete ubuntu:16.04 bash 进入容器 docker exec -it flow_mete bash cd ~ 安装基本软件 ......

messud4312 ⋅ 今天 ⋅ 0

OSChina 周一乱弹 —— 快别开心了,你还没有女友呢。

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @莱布妮子 :分享吴彤的单曲《好春光》 《好春光》- 吴彤 手机党少年们想听歌,请使劲儿戳(这里) @clouddyy :小萝莉街上乱跑,误把我认错成...

小小编辑 ⋅ 今天 ⋅ 9

Java 开发者不容错过的 12 种高效工具

Java 开发者常常都会想办法如何更快地编写 Java 代码,让编程变得更加轻松。目前,市面上涌现出越来越多的高效编程工具。所以,以下总结了一系列工具列表,其中包含了大多数开发人员已经使用...

jason_kiss ⋅ 昨天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部