Java NIO

I/O简介 

  I/O问题可以说是当今web应用中所面临的的主要问题之一,大部分的web应用系统的瓶颈都是I/O瓶颈。这个系列主要介绍JAVA的I/O类库基本架构、磁盘I/O工作机制、网络I/O工作机制以及NIO的工作方式。

BIO(Block IO)和Nio(Non-Block IO)的对比

IO模式 BIO NIO
方式 从磁盘到磁盘 从缓存到磁盘
通信 缓存(多级复用)
处理 阻塞 非阻塞(Reactor)
触发 选择器轮询机制

 

从1.4版本开始JAVA引入了NIO,用来提升I/O性能。I/O操作类在包java.io下,大概有将近80个类

这些类可以分为如下四组:

  基于字节操作的I/O接口:InputStream和OutputStream

  基于字符操作的I/O接口:Reader和Writer

  基于磁盘操作的I/O接口:File

  基于网络操作的I/O接口:Socket

前两组是传输数据的格式,后两组是传输数据的方式

核心组件

核心组件 定义 作用 特点 使用

通道

(Channel)

NIO数据的源头/目的地

(Buffer的唯一接口)

向缓存提供数据

读取换区的数据

双向读写,异步读写

数据来源/流向 总是Buffer

根据来源区别:

FileChannel:从文件

DataChannel:从UDP网络数据

SocketChannel:

ServerSocketChannel

缓存

(Buffer)

NIO数据读/写中转地

(一块完整的内存块)

数据缓存 适用于处布尔外基本数据类型

7种基本数据类型

 

选择器

(Selector)

异步IO核心类 实现异步非阻塞IO

使用一个Selector线程检测1个/多个

通道channel上的事件,给予事件驱动

分发不需要为每个channel去分配一个线程

1、创建Selector对象

2、箱Slector注册通道Channel

2、调用Selector类中select()方法

一、Buffer缓存区

1) Buffer介绍: 缓冲区,本质就是一个数组,但是它是特殊的数组,缓冲区对象内置了一些机制,能够追踪和记录缓冲区的状态变化情况,如果我们使用get方法从   缓冲区中获取数据或者用put方法吧数据写入缓冲区,都会引起缓冲区的状态变化
在缓冲区中,最重要的属性是如下三个,他们一起合作完成了对缓冲区内容状态的变化跟踪
1)position:指定了下一个将要被写入或者读取的元素索引,它的值由get()/put() 方法自动更新,在新创建一个Buffer对象时,position被初始化为0
2)limit:操作缓冲区的可操作空间和可操作范围,指定还有多少数据需要去除,或者还有多少空间可以放入数据
3)capacity:指定了可以存储在缓冲区中的最大数据容量,实际上,它指定了底层数组的大小,或者至少是指定了准许我们使用的底层数组的容量。

以上三个属性值之间有一些相对的大小的关系:0<=position<=limit<=capacity

package com.Allen.buffer;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class testBufferDemo01 {
    public static void main(String[] args) throws IOException {
        String fileURL="F://a.txt";
        FileInputStream fis=new FileInputStream(fileURL);
        //获取通路
        FileChannel channel=fis.getChannel();
        //定义缓冲区大小
        ByteBuffer buffer=ByteBuffer.allocate(10);
        output("init", buffer);
        //先读
        channel.read(buffer);
        output("read", buffer);
        buffer.flip();
        output("flip", buffer);        
        while (buffer.hasRemaining()) {
            byte b=buffer.get();
        }
        output("get", buffer);
        buffer.clear();
        output("clear", buffer);
        fis.close();
    }
    
    public static void output(String string,ByteBuffer buffer){
        System.out.println(string);
        System.out.println(buffer.capacity()+":"+buffer.position()+":"+buffer.limit());
    }
}

二、Channel(通路)

任何时候读取数据,都不是直接从通道中读取,而是从通道读取到缓冲区,所以使用NIO读取数据可以分成下面三个步骤
1)从FileInputStream获取Channel
2)创建Buffer
3)将数据从Channel 读取到Buffer中

package com.allen.test;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class testNio {
    public static void main(String[] args) throws IOException {
        String oldFileUrl="E://1.txt";
        String newFileUrl="E://2.txt";
        FileInputStream fis=new FileInputStream(oldFileUrl);
     //1、获取Channel FileChannel inChannel
=fis.getChannel();
     //2、创建Buffer ByteBuffer bf
=ByteBuffer.allocate(1024); FileOutputStream fos=new FileOutputStream(newFileUrl); FileChannel outChannel=fos.getChannel(); while(true){ int eof=inChannel.read(bf); if(eof==-1){ break; }else{ bf.flip();
     //3、把数据写入缓存 outChannel.write(bf); bf.clear(); } } inChannel.close(); fis.close(); outChannel.close(); fos.close(); } }

三、Selector(选择器)

  Selector 一般称 为选择器 ,也即多路复用器 。它是Java NIO核心组件中的一个,用于检查一个或多个NIO Channel(通道)的状态是否处于可读、可写。如此可以实现单线程管理多个channels,也就是可以管理多个网络链接。
   使用Selector的好处: 使用更少的线程来就可以来处理通道了, 相比使用多个线程,避免了线程上下文切换带来的开销。
 // 1. 创建Selector对象   
 Selector sel = Selector.open();

 // 2. 向Selector对象绑定通道   
 // a. 创建可选择通道,并配置为非阻塞模式   
 ServerSocketChannel server = ServerSocketChannel.open();   
 server.configureBlocking(false);   
 
 // b. 绑定通道到指定端口   
 ServerSocket socket = server.socket();   
 InetSocketAddress address = new InetSocketAddress(port);   
 socket.bind(address);   
 
 // c. 向Selector中注册感兴趣的事件   
 server.register(sel, SelectionKey.OP_ACCEPT);    
 return sel;

// 3. 处理事件
try {    
    while(true) { 
        // 该调用会阻塞,直到至少有一个事件就绪、准备发生 
        selector.select(); 
        // 一旦上述方法返回,线程就可以处理这些事件
        Set<SelectionKey> keys = selector.selectedKeys(); 
        Iterator<SelectionKey> iter = keys.iterator(); 
        while (iter.hasNext()) { 
            SelectionKey key = (SelectionKey) iter.next(); 
            iter.remove(); 
            process(key); 
        }    
    }    
} catch (IOException e) {    
    e.printStackTrace();   
}

总结nio工作原理

1)由一个专门的线程去处理所有的io事件并且负责分发
2)事件驱动机制,时间到的时候触发,而不是同步地去监听事件
3)线程通信,线程之间通过wait,notify等方式通信,保证每次上下文切换都是有意义的,减少无畏的线程切换。
posted @ 2018-12-28 17:19  Chichens  阅读(153)  评论(0编辑  收藏  举报