java基础之NIO

概述

nio是jdk1.4之后引入的技术,现在nio的使用场景比较常见,如tomcat处理连接,所以我们很有必要掌握它。

要认识nio就必然要了解这个体系中的三大核心部分,Channel, Buffer, Selector。 传统的io是面向流进行读写,而nio是把数据从Channel中读取到Buffer中,或者从Buffer中把数据写到Channel中。Selector在socket编程中用于分发事件如,建立连接,数据到达等。这让其能用一个线程来管理多个通道。bio在的一些方法会导致阻塞,如accept(), read(),它会一直阻塞直到有连接或者有数据可以被读取,但是在nio中的select(),read(),并不会导致阻塞,若没有事件 或可以供读取的数据时候它们会立即返回一个值,然后线程可以去处理其它的事情。这也是为什么说bio是阻塞的,nio是非阻塞的。

组件

Channel

在传统io中,我们面向文件建立流,如inputstream outputstream,它是单向的,但是在nio中 channel是双向的,它既可以写也可以读。在nio中我们常用的Channel的实现 类主要有如下几种

  1. FileChannel
  2. SocketChannel
  3. ServerSocketChannel

通常,我们做读写,主要就是处理从文件和从网络两个方向来的数据,以上的实现也是如此。

Buffer

在nio中,对于每一种基本数据类型都有着一种对应的Buffer,如ByteBuffer,IntBuffer等,它的存在帮助我们能灵活的对数据进行读取。它的底层实现就是一个数组,他还有几个重要的标志量

属性 说明
capacity 缓存去数组的总长度
position 下一个要操作的数据元素的位置
limit 缓存区数组中不可操作的下一个元素的位置:limit <= capacity
mark 用于记录当前position的前一个位置或者默认是-1

接下来我们可以看看缓存的几个重要方法
首先当然是分配空间,前面说,他的底层是一个数组,所以是要设置大小的,用ByteBuffer举例

ByteBuffer byteBuffer = ByteBuffer.allocate(1024); //

fileInChannel.read(byteBuffer) //注意这里是channel调用的读方法,即从通道读取数据,然后写到缓存中

byteBuffer.flip() 

fileOutchannel.write(byteBuffer);//把从缓冲读取的数据写到通道中去

byteBuffer.clear();// 清空数组 重置标志量

如上的伪代码,在分配了1k空间后,capacity和limit都是1024,position是0,mark是-1等我们从通道往这个缓存写入100byte数据后,capacity和limit不变,position变成了100,mark是99
为了开始读取缓存中的数据,我们执行了flip方法,这时候,capacity不变,limit变成了100, pchuangosition变成了0,mark变成了-1,当我们执行write的过程中,position的大小开始增加,直到读完,等于limit的大小,即100。

Selector

Selector也是一个非常重要的部分,在一些连接量大,但是每个连接的传输数据量较小的场景,若用传统io,那么会对于每一个请求我们都要用开启一个线程去处理,这样会占用大量的系统资源,可能导致后面的请求无法得到处理。但是nio在这种场景非常适合,它通过Selector来处理分发事件,管理多个通道,并不是每一个通道每时每刻都有数据需要读写,对于没有读写的通道,我们的线程不用阻塞,而是可以去处理其它的通道io。

实例

文件

    public static void method1(){
        RandomAccessFile aFile = null;
        try{
            aFile = new RandomAccessFile("src/nio.txt","rw");
            FileChannel fileChannel = aFile.getChannel();
            ByteBuffer buf = ByteBuffer.allocate(1024);
 
            int bytesRead = fileChannel.read(buf);
            System.out.println(bytesRead);
 
            while(bytesRead != -1)
            {
                buf.flip();
                while(buf.hasRemaining())
                {
                    System.out.print((char)buf.get());
                }
 
                buf.compact();
                bytesRead = fileChannel.read(buf);
            }
        }catch (IOException e){
            e.printStackTrace();
        }finally{
            try{
                if(aFile != null){
                    aFile.close();
                }
            }catch (IOException e){
                e.printStackTrace();
            }
        }
    }

网络

public class ServerConnect
{
    private static final int BUF_SIZE=1024;
    private static final int PORT = 8080;
    private static final int TIMEOUT = 3000;
 
    public static void main(String[] args)
    {
        selector();
    }
 
    public static void handleAccept(SelectionKey key) throws IOException{
        ServerSocketChannel ssChannel = (ServerSocketChannel)key.channel();
        SocketChannel sc = ssChannel.accept();
        sc.configureBlocking(false);
        sc.register(key.selector(), SelectionKey.OP_READ,ByteBuffer.allocateDirect(BUF_SIZE));
    }
 
    public static void handleRead(SelectionKey key) throws IOException{
        SocketChannel sc = (SocketChannel)key.channel();
        ByteBuffer buf = (ByteBuffer)key.attachment();
        long bytesRead = sc.read(buf);
        while(bytesRead>0){
            buf.flip();
            while(buf.hasRemaining()){
                System.out.print((char)buf.get());
            }
            System.out.println();
            buf.clear();
            bytesRead = sc.read(buf);
        }
        if(bytesRead == -1){
            sc.close();
        }
    }
 
    public static void handleWrite(SelectionKey key) throws IOException{
        ByteBuffer buf = (ByteBuffer)key.attachment();
        buf.flip();
        SocketChannel sc = (SocketChannel) key.channel();
        while(buf.hasRemaining()){
            sc.write(buf);
        }
        buf.compact();
    }
 
    public static void selector() {
        Selector selector = null;
        ServerSocketChannel ssc = null;
        try{
            selector = Selector.open();
            ssc= ServerSocketChannel.open();
            ssc.socket().bind(new InetSocketAddress(PORT));
            ssc.configureBlocking(false);
            ssc.register(selector, SelectionKey.OP_ACCEPT);
 
            while(true){
                if(selector.select(TIMEOUT) == 0){
                    System.out.println("==");
                    continue;
                }
                Iterator<SelectionKey> iter = selector.selectedKeys().iterator();
                while(iter.hasNext()){
                    SelectionKey key = iter.next();
                    if(key.isAcceptable()){
                        handleAccept(key);
                    }
                    if(key.isReadable()){
                        handleRead(key);
                    }
                    if(key.isWritable() && key.isValid()){
                        handleWrite(key);
                    }
                    if(key.isConnectable()){
                        System.out.println("isConnectable = true");
                    }
                    iter.remove();
                }
            }
 
        }catch(IOException e){
            e.printStackTrace();
        }finally{
            try{
                if(selector!=null){
                    selector.close();
                }
                if(ssc!=null){
                    ssc.close();
                }
            }catch(IOException e){
                e.printStackTrace();
            }
        }
    }
}

参照文章

nio高效的原理 彻底搞清楚nio高效的原理

攻破nio技术壁垒攻破nio技术壁垒

你可能感兴趣的:(JAVA基础)