Java NIO Tutorial 地址:http://tutorials.jenkov.com/java-nio/index.html

Java NIO系列教程译文地址:http://ifeve.com/java-nio-all/

以下是我拜读过程中摘抄的部分内容,并且加了一些内容、笔记,姑且叫《快学Java NIO》,方便以后再翻阅学习

附上一个Java NIO实现的demo,多人网络聊天室

Java NIO 由以下几个核心部分组成:

  • Channels
  • Buffers
  • Selectors

基本上,所有的 IO 在NIO 中都从一个Channel 开始。Channel 有点像流。 数据可以从Channel读到Buffer中,也可以从Buffer 写到Channel中。

Channel

FileChannel 从文件中读写数据。

DatagramChannel 能通过UDP读写网络中的数据。

SocketChannel 能通过TCP读写网络中的数据。

ServerSocketChannel可以监听新进来的TCP连接,像Web服务器那样。对每一个新进来的连接都会创建一个SocketChannel。

下面是一个FileChannel的示例

public class FileChannelTest {
public static void main(String[] args) throws IOException {
RandomAccessFile aFile = new RandomAccessFile("data/nio-data.txt", "rw");
FileChannel inChannel = aFile.getChannel();
    
//涉及到的buffer的方法稍后解释
ByteBuffer buf = ByteBuffer.allocate(48); int bytesRead = inChannel.read(buf);
while (bytesRead != -1) { //make buffer ready for read
buf.flip(); while (buf.hasRemaining()) {
System.out.print((char) buf.get());// read 1 byte at a time
}
buf.clear();//buf.compact();也可以
bytesRead = inChannel.read(buf);
}
aFile.close();
}
}

Buffer

为了理解Buffer的工作原理,需要熟悉它的三个属性:

  • capacity
  • position
  • limit

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

当切换Buffer到读模式时, limit表示你最多能读到多少数据。因此,当切换Buffer到读模式时,limit会被设置成写模式下的position值。换句话说,你能读到之前写入的所有数据(limit被设置成已写数据的数量,这个值在写模式下就是position)

clear方法就是让position设回0,limit与capacity相等。

  public final Buffer clear() {
position = 0;
limit = capacity;
mark = -1;
return this;
}

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

  public final Buffer flip() {
limit = position;
position = 0;
mark = -1;
return this;
}

compact()方法将所有未读的数据拷贝到Buffer起始处。然后将position设到最后一个未读元素正后面。limit属性依然像clear()方法一样,设置成capacity。现在Buffer准备好写数据了,但是不会覆盖未读的数据。

  public ByteBuffer compact() {

        System.arraycopy(hb, ix(position()), hb, ix(0), remaining());
position(remaining());
limit(capacity());
discardMark();
return this; }

Scatter/Gather

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

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

应用场景:例如传输一个由消息头和消息体组成的消息,你可能会将消息体和消息头分散到不同的buffer中,这样你可以方便的处理消息头和消息体

Scattering Reads在移动下一个buffer前,必须填满当前的buffer,这也意味着它不适用于动态消息(译者注:消息大小不固定)。

ByteBuffer header = ByteBuffer.allocate(128);
ByteBuffer body = ByteBuffer.allocate(1024); ByteBuffer[] bufferArray = { header, body }; channel.read(bufferArray);
ByteBuffer header = ByteBuffer.allocate(128);
ByteBuffer body = ByteBuffer.allocate(1024); //write data into buffers ByteBuffer[] bufferArray = { header, body }; channel.write(bufferArray);

FileChannel的transferFrom()方法可以将数据从源通道传输到FileChannel中,下面是一个简单的例子:

RandomAccessFile fromFile = new RandomAccessFile("data/fromFile.txt", "rw");
FileChannel fromChannel = fromFile.getChannel(); RandomAccessFile toFile = new RandomAccessFile("data/toFile.txt", "rw");
FileChannel toChannel = toFile.getChannel(); long position = 0;
long count = fromChannel.size(); //toChannel.transferFrom(fromChannel, position, count);也可以
fromChannel.transferTo(position, count, toChannel);

Selector

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

Selector selector = Selector.open();
//FileChannel不能切换到非阻塞模式,所以这边不能使FileChannel
channel.configureBlocking(false);//与Selector一起使用时,Channel必须处于非阻塞模式下
SelectionKey key = channel.register(selector, SelectionKey.OP_READ); //除了注册读,还可以注册connect,accept,read,write事件
while(true) {
int readyChannels = selector.select(); //阻塞到至少有一个通道就绪,还有select(long timeout)超时就不阻塞,selectNow()不阻塞,没有就返回0,当然打断阻塞还有wakeUp()方法,可以用另外一个线程调用这个方法,操作同一个selector对象即可
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.
     //SelectionKey.channel()方法返回的通道需要转型成你要处理的类型,如ServerSocketChannel或SocketChannel等
} 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
}
  //Selector不会自己从已选择键集中移除SelectionKey实例。必须在处理完通道时自己移除。下次该通道变成就绪时,Selector会再次将其放入已选择键集中。
keyIterator.remove();
}
}

现在能看到的情况是,一个请求过来,到Selector这边,selector从注册的通道中选择就绪的通道,然后找到具体的通道处理这个请求。

用一个selector线程来安排所有的channel!

当然为了并发,可以用多个selector,然后不同的channel来注册。这样就有了反向代理的感觉,selector就是反向代理服务器上的线程!

(以上是我个人对selector的理解,若理解有误,请指正)

Java NIO与IO

我应该何时使用IO,何时使用NIO呢?在本文中,我会尽量清晰地解析Java NIO和IO的差异、它们的使用场景,以及它们如何影响您的代码设计。

Java NIO与IO之间主要差别

IO                NIO
面向流 面向缓冲
阻塞IO 非阻塞IO
无 选择器

Java NIO的缓冲导向方法是数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动,这就增加了处理过程中的灵活性。NIO设计中多了buffer,传统IO如果要这个效果,需要自行定义操作buffer。

Java NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取。

Java NIO的选择器允许一个单独的线程来监视多个输入通道,你可以注册多个通道使用一个选择器。

在IO设计中,我们从InputStream或 Reader逐字节读取数据。 readline()阻塞直到整行读完

NIO可让您只使用一个(或几个)单线程管理多个通道(网络连接或文件),但付出的代价是解析数据可能会比从一个阻塞流中读取数据更复杂。

如果需要管理同时打开的成千上万个连接,这些连接每次只是发送少量的数据,例如聊天服务器,实现NIO的服务器可能是一个优势。

如果你需要维持许多打开的连接到其他计算机上,如P2P网络中,使用一个单独的线程来管理你所有出站连接,可能是一个优势。

Java NIO: 单线程管理多个连接,如下图

如果你有少量的连接使用非常高的带宽,一次发送大量的数据,也许典型的IO服务器实现可能非常契合。

Java IO: 一个典型的IO服务器设计- 一个连接通过一个线程处理,如下图

至此,基本上Java NIO的大体轮廓已经明白了,鉴于篇幅不要太长,各个具体Channel的介绍移步:快学Java NIO续篇

  • FileChannel
  • SocketChannel
  • ServerSocketChannel
  • Java NIO DatagramChannel
  • Pipe

快学Java NIO的更多相关文章

  1. 快学Java NIO 续篇

    可以先看Java NIO的整体介绍,这篇接着说以下内容,<快学Java NIO>续篇 FileChannel SocketChannel ServerSocketChannel Java ...

  2. JAVA NIO之文件通道

    1.简介 通道是 Java NIO 的核心内容之一,在使用上,通道需和缓存类(ByteBuffer)配合完成读写等操作.与传统的流式 IO 中数据单向流动不同,通道中的数据可以双向流动.通道既可以读, ...

  3. 全面解读Java NIO工作原理(1)

    全面解读Java NIO工作原理(1) 2011-12-14 10:31 Rollen Holt Rollen Holt的博客 我要评论(0) 字号:T | T JDK 1.4 中引入的新输入输出 ( ...

  4. Java NIO 基础知识

    前言 前言部分是科普,读者可自行选择是否阅读这部分内容. 为什么我们需要关心 NIO?我想很多业务猿都会有这个疑问. 我在工作的前两年对这个问题也很不解,因为那个时候我认为自己已经非常熟悉 IO 操作 ...

  5. JAVA NIO之浅谈内存映射文件原理与DirectMemory

    JAVA类库中的NIO包相对于IO 包来说有一个新功能是内存映射文件,日常编程中并不是经常用到,但是在处理大文件时是比较理想的提高效率的手段.本文我主要想结合操作系统中(OS)相关方面的知识介绍一下原 ...

  6. 【aliyun】学java,看这里,不迷茫!1460道Java热门问题

    阿里极客公益活动: 或许你挑灯夜战只为一道难题 或许你百思不解只求一个答案 或许你绞尽脑汁只因一种未知 那么他们来了,阿里系技术专家来云栖问答为你解答技术难题了 他们用户自己手中的技术来帮助用户成长 ...

  7. 史上最强Java NIO入门:担心从入门到放弃的,请读这篇!

    本文原题“<NIO 入门>,作者为“Gregory M. Travis”,他是<JDK 1.4 Tutorial>等书籍的作者. 1.引言 Java NIO是Java 1.4版 ...

  8. JAVA NIO 内存映射(转载)

    原文地址:http://blog.csdn.net/fcbayernmunchen/article/details/8635427     Java类库中的NIO包相对于IO 包来说有一个新功能是内存 ...

  9. java NIO 随笔

    一,NIO入门    NIO 是new io的缩写,说实话,nio api比较难用,所用大家需要采用网络通信的时候,普通首先想到的是netty,不直接使用NIO,但是你不了解NIO,说实话,你也理解不 ...

随机推荐

  1. spring AOP 的几种实现方式(能测试)

    我们经常会用到的有如下几种 1.基于代理的AOP 2.纯简单Java对象切面 3.@Aspect注解形式的 4.注入形式的Aspcet切面 一.需要的java文件 public class ChenL ...

  2. python基础——枚举类

    python基础——枚举类 当我们需要定义常量时,一个办法是用大写变量通过整数来定义,例如月份: JAN = 1 FEB = 2 MAR = 3 ... NOV = 11 DEC = 12 好处是简单 ...

  3. Angular.JS

    AngularJS是什么? 完全使用 JavaScript编写的客户端技术.同其他历史悠久的 Web技术( HTML. CSS 和JavaScript)配合使用,使Web应用开发比以往更简单.更快捷. ...

  4. 二、JavaScript语言--JS动画--JS动画效果

    运动框架实现思路: 1.速度(改变值:left , right , width , height , opacity) 2.缓冲运动 3.多物体运动 4.任意值改变 5.链式运动 6.同时运动 js用 ...

  5. 使用Delphi对象(声明、实例化、构造、释放)

    一.声明和实例化 在使用一个对象之前,用class关键字声明一个对象.可以在一个程序或单元的type部分声明一个对象类型: type TFooObject = class; 除了声明一个对象类型,通常 ...

  6. ckplayer视频播放插件使用

    研究ckplayer插件播放视频,播放视频需要配置信息修改如下: 1.设置ckplayer.js中的logo: 'null' 可以隐藏视频播放头部的图标: 2.设置ckplayer.js中的ckcpt ...

  7. linux命令**50

    1.ls命令 命令格式: ls [选项] [目录名] 命令功能: 列出目标目录中所有的子目录和文件. 常用参数: -a,列出所有文件包括隐藏文件 -l,列出详细信息,文件大小一般以字节大小显示 -h, ...

  8. poj 1005:I Think I Need a Houseboat(水题,模拟)

    I Think I Need a Houseboat Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 85149   Acce ...

  9. html5 Canvas绘制图形入门详解

    html5,这个应该就不需要多作介绍了,只要是开发人员应该都不会陌生.html5是「新兴」的网页技术标准,目前,除IE8及其以下版本的IE浏览器之外,几乎所有主流浏览器(FireFox.Chrome. ...

  10. golang level

    exp = (currentLevel-1) * 501 02 503 1004 150startLevel = 1currentLevel = 2currentExp = 0