NIO基本操作

Posted grasp

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了NIO基本操作相关的知识,希望对你有一定的参考价值。

NIO是Java 4里面提供的新的API,目的是用来解决传统IO的问题

NIO主要有三大核心部分:Channel(通道),Buffer(缓冲区), Selector(选择器)

Channel(通道)

通道:类似于流,但是可以异步读写数据(流只能同步读写),通道是双向的,(流是单向的),通道的数据总是要先读到一个buffer 或者 从一个buffer写入,即通道与buffer进行数据交互。

  通道类型:  

FileChannel:从文件中读写数据。  

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

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

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

  通过使用FileChannel可以从文件读或者向文件写入数据;通过SocketChannel,以TCP来向网络连接的两端读写数据;通过ServerSocketChanel能够监听客户端发起的TCP连接,并为每个TCP连接创建一个新的SocketChannel来进行数据读写;通过DatagramChannel,以UDP协议来向网络连接的两端读写数据。

Buffer(缓冲区)

缓冲区 本质上是一块可以存储数据的内存,被封装成了buffer对象而已!

缓冲区类型:

ByteBuffer

MappedByteBuffer

CharBuffer  

DoubleBuffer  

FloatBuffer  

IntBuffer  

LongBuffer  

ShortBuffer 

常用方法:

allocate() - 分配一块缓冲区  

put() -  向缓冲区写数据

get() - 向缓冲区读数据  

filp() - 将缓冲区从写模式切换到读模式  

clear() - 从读模式切换到写模式,不会清空数据,但后续写数据会覆盖原来的数据,即使有部分数据没有读,也会被遗忘;  

compact() - 从读数据切换到写模式,数据不会被清空,会将所有未读的数据copy到缓冲区头部,后续写数据不会覆盖,而是在这些数据之后写数据

mark() - 对position做出标记,配合reset使用

reset() - 将position置为标记值 

hasRemaining() - 判断是否还有可用数据

缓冲区的一些属性:

capacity - 缓冲区大小,无论是读模式还是写模式,此属性值不会变;

position - 写数据时,position表示当前写的位置,每写一个数据,会向下移动一个数据单元,初始为0;最大为capacity - 1切换到读模式时,position会被置为0,表示当前读的位置

limit - 写模式下,limit 相当于capacity 表示最多可以写多少数据,切换到读模式时,limit 等于原先的position,表示最多可以读多少数据。

Selector(选择器)

选择器:相当于一个观察者,用来监听通道感兴趣的事件,一个选择器可以绑定多个通道;

通道向选择器注册时,需要指定感兴趣的事件,选择器支持以下事件:

SelectionKey.OP_CONNECT

SelectionKey.OP_ACCEPT

SelectionKey.OP_READ

SelectionKey.OP_WRITE  

 如果你对不止一种事件感兴趣,那么可以用“位或”操作符将常量连接起来,如下:

     int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE; 

 通道向选择器注册时,会返回一个 SelectionKey对象,具有如下属性

interest集合

ready集合  

Channel  

Selector

附加的对象(可选) 

用“位与”操作interest 集合和给定的SelectionKey常量,可以确定某个确定的事件是否在interest 集合中。

int interestSet = selectionKey.interestOps();  
 
boolean isInterestedInAccept  = interestSet & SelectionKey.OP_ACCEPT;
boolean isInterestedInConnect = interestSet & SelectionKey.OP_CONNECT; boolean isInterestedInRead = interestSet & SelectionKey.OP_READ; boolean isInterestedInWrite = interestSet & SelectionKey.OP_WRITE;

ready 集合是通道已经准备就绪的操作的集合。在一次选择(Selection)之后,你会首先访问这个ready set。Selection将在下一小节进行解释。可以这样访问ready集合: 
  int readySet = selectionKey.readyOps();

也可以使用以下四个方法获取已就绪事件,返回值为boolean:

selectionKey.isAcceptable();  
selectionKey.isConnectable();  
selectionKey.isReadable();  
selectionKey.isWritable();  

可以将一个对象或者更多信息附着到SelectionKey上,即记录在附加对象上,方法如下:

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

可以通过选择器的select方法获取是否有就绪的通道;

int select()  

int select(long timeout)  

int selectNow()

返回值表示上次执行select之后,就绪通道的个数。 

可以通过selectedKeySet获取已就绪的通道。返回值是SelectionKey 的集合,处理完相应的通道之后,需要removed 因为Selector不会自己removed

select阻塞后,可以用wakeup唤醒;执行wakeup时,如果没有阻塞的select  那么执行完wakeup后下一个执行select就会立即返回。

调用close() 方法关闭selector

传统IO和NIO简单例子

传统IO读取文件:

public static void ioRead() {
        File file = new File("f:/io.txt");
        InputStream in = null;
        try {
            in = new FileInputStream(file);
            byte[] bytes = new byte[1024];
            in.read(bytes);
            System.out.println(new String(bytes));

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

NIO操作

public static void nioRead() {
        RandomAccessFile aFile = null;
        try {
            aFile = new RandomAccessFile("F:/io.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();
            }
        }
    }

 



以上是关于NIO基本操作的主要内容,如果未能解决你的问题,请参考以下文章

Java map / nio / NFS 问题导致 VM 故障:“在最近编译的 Java 代码中的不安全内存访问操作中发生故障”

Java异步非阻塞IO NIO使用与代码分析

VSCode自定义代码片段——git命令操作一个完整流程

JavaJava NIO

Day386&387.Nio概述&Nio的Channel通道 -NIO

Java NIO