zoukankan      html  css  js  c++  java
  • 初识 Java NIO

    一、初识nio

      在 JDK 1. 4 中 新 加入 了 NIO( New Input/ Output) 类, 引入了一种基于通道和缓冲区的 I/O 方式,它可以使用 Native 函数库直接分配堆外内存,然后通过一个存储在 Java 堆的 DirectByteBuffer 对象作为这块内存的引用进行操作,避免了在 Java 堆和 Native 堆中来回复制数据。

      NIO 是一种同步非阻塞的 IO 模型。同步是指线程不断轮询 IO 事件是否就绪,非阻塞是指线程在等待 IO 的时候,可以同时做其他任务。同步的核心就是 Selector,Selector 代替了线程本身轮询 IO 事件,避免了阻塞同时减少了不必要的线程消耗;非阻塞的核心就是通道和缓冲区,当 IO 事件就绪时,可以通过写道缓冲区,保证 IO 的成功,而无需线程阻塞式地等待。

    二、NIO中的重要概念

      1、缓冲区(buffer)

      NIO是基于缓冲区的IO方式。当一个链接建立完成后,IO的数据未必会马上到达,为了使数据到达时能够正确完成IO操作,在BIO(阻塞IO)中,等待IO的线程必须被阻塞,以全天候地执行IO操作。为了解决这种IO方式低效的问题,引入了缓冲区的概念,当数据到达时,可以预先被写入缓冲区,再由缓冲区交给线程,因此线程无需阻塞地等待。

      常用冲区类型

       ByteBuffer、MappedByteBuffer、CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer、ShortBuffer  

      缓冲区常用方法:

       allocate() - 分配一块缓冲区  

       put() -  向缓冲区写数据

       get() - 向缓冲区读数据  

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

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

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

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

       reset() - 将position置为标记值    

      缓冲区的一些属性:

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

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

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

      2、通道

      通道是 I/O 传输发生时通过的入口,而缓冲区是这些数据传输的来源或目标。对于离开缓冲区的传输,您想传递出去的数据被置于一个缓冲区,被传送到通道。对于传回缓冲区的传输,一个通道将数据放置在您所提供的缓冲区中。

      例如:有一个服务器通道 ServerSocketChannel serverChannel,一个客户端通道 SocketChannel clientChannel;服务器缓冲区:serverBuffer,客户端缓冲区:clientBuffer。当服务器想向客户端发送数据时,需要调用:clientChannel.write(serverBuffer)。当客户端要读时,调用 clientChannel.read(clientBuffer);当客户端想向服务器发送数据时,需要调用:serverChannel.write(clientBuffer)。当服务器要读时,调用 serverChannel.read(serverBuffer)。

      常用通道类型

       FileChannel:从文件中读写数据。  

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

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

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

      3、选择器(selector)

      通道和缓冲区的机制,使得线程无需阻塞地等待IO事件的就绪,但是总是要有人来监管这些IO事件。这个工作就交给了selector来完成,这就是所谓的同步。要使用Selector,得向Selector注册Channel,然后调用它的select()方法。这个方法会一直阻塞到某个注册的通道有事件就绪,这就是所说的轮询。一旦这个方法返回,线程就可以处理这些事件。

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

       SelectionKey.OP_CONNECT

       SelectionKey.OP_ACCEPT

       SelectionKey.OP_READ

       SelectionKey.OP_WRITE

    三、小实例

      使用nio简单实现了文件的复制。

    public class Test {
        
        public static void main(String[] args) {
            FileInputStream fin = null;
            FileOutputStream fout = null;
            FileChannel fic = null;
            FileChannel foc = null;
            try {
                fin = new FileInputStream("F:\1.txt");
                fout = new FileOutputStream("F:\2.txt");
                
                //从FileInputStream创建用于输入的FileChannel
                fic = fin.getChannel();
                
                //从FileOutputStream创建用于输出的FileChannel
                foc = fout.getChannel();
                
                //建立buffer缓冲区,2的8次方
                ByteBuffer buf = ByteBuffer.allocate(1024<<8);
                
                //根据read返回实际独处的字节数,终止循环
                //缓冲区从fic读取数据
                while(fic.read(buf)>0) {
                    // 缓冲区翻转用于输出数据到focus
                    buf.flip();
                    foc.write(buf);
                    //清空缓冲区用于下次读取
                    buf.clear();
                }
                
                //安全释放资源
                if(fic != null)
                    fic.close();
                if(foc != null)
                    foc.close();
                if(fin != null)
                    fin.close();
                if(fout != null)
                    fout.close();
                
            } catch (FileNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } finally {
                
            }
        }
    
    }

    PS:因本人能力有限,如有误还请谅解;

  • 相关阅读:
    关于swift 单元测试的补充
    架构设计案例分析-高速公路收费运营管理平台
    可以落地的软件架构
    循序渐进地培养面向对象的思维方式
    动态规划初学
    求解惑
    github eclipse项目
    关于x86 i586之类
    Manifest intent filter翻译
    消息处理机制小结
  • 原文地址:https://www.cnblogs.com/WHL5/p/9418422.html
Copyright © 2011-2022 走看看