文档章节

java的NIO

清尘V
 清尘V
发布于 2016/05/11 13:42
字数 1748
阅读 45
收藏 1
点赞 2
评论 0

java的NIO

        java的NIO主要有3个特性Channel、buffer、selector来保证I/O高可复用性,其中最重要的是buffer和selector操作。详细教材查看 jakob jenkov教材:http://tutorials.jenkov.com/java-nio/index.html 

1、channel和buffer

           a、 channel:有点像流的管道,NIO从channel里面获取、发送数据。java的I/O已经从底层被NIO实现了一次,所以性能上和纯粹的NIO中使用channel没有太大的区别。

        channel的类型主要就下面几种:

FileChannel            //文件
DatagramChannel        //UDP
SocketChannel          //socket
ServerSocketChannel    //socket服务

它有2个特点:1、读写控制、2、按着单个byte位来操作。           b、 Buffer:就是在内存开辟的空间,用来临时存放数据。这里的buffer是以bytebuffer为父类实现的HeapByteBuffer。

            一个Buffer有3个中有的参数:

                position:当前位置

                            1、写:从当前可以写的地址开始(第一次从0开始),随着写入的增大。写的时候最大为capacity-1。

                            2、读:从0开始、随着读开始移动增大。最大读取到limit

                limit:写模式下为capacity。当转换为读模式,则limit=position(写入的个数)

                capacity:一个buffer的固定大小。

        如下示意图

    例子:

#有一个Buffer是70字节
1、buffer.allocate(70):capacity=70、position=0、limit=70
1、写如40个字节:capacity=70、position=40、limit=70
2、写转化为读:buffer.flip();capacity=70,position=0,limit=40
3、读30字节:capacity=70、position=30、limit=40
4、读转写:
       buffer.clear():所有剩余数据都清空。capacity=70,position=0,limit=70
       buffer.compact():将剩余的所有数据复制到buffer起始。capacity=70,position=10,limit=70

   一个fileChannel的例子

public static void main(String[] args) {
    try {
        RandomAccessFile aFile = new RandomAccessFile("D:/project/test/nio/1.txt", "rw");
        FileChannel fileChannel = aFile.getChannel();
        // buffer
        ByteBuffer buf = ByteBuffer.allocate(48);
        while (fileChannel.read(buf) != -1) {
            buf.flip();// 写模式切换成读模式
            while (buf.hasRemaining()) {
                System.out.println(buf.get());
            }
            buf.clear();
        }
        aFile.close();//从写切换到读
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}


    c、buffer的分类:

 

2、零复制

        buffer默认是在JVM开辟空间、而NIO比BIO在数据处理方面有一个有点:不会将数据从逻辑主存复制到JVM主存

bytebuffer开辟空间的两个方法。第二个方法直接在主存开辟空间、不需要在JVM中操作

    1. 直接从内核态的数据区读取数据,不用在copy到jvm堆内存。

    2. 多个数据Buffer不用组合成一个,直接就程序处理了。

    3. 发送的时候,直接发送。

//直接在JVM开辟空间
public static ByteBuffer allocate(int capacity) {
        if (capacity < 0)
            throw new IllegalArgumentException();
        return new HeapByteBuffer(capacity, capacity);
}   
//直接在内存开辟空间
public static ByteBuffer allocateDirect(int capacity) {
        return new DirectByteBuffer(capacity);
}


3、Slector

        普通的I/O调用都会阻塞等待,直到文件数据准备就行才能使用。而NIO则是通过一个单独的线程不对的去询问系统I/O数据是否准备好了。准备好后,就可以通过存放在Selector线程中的key(处理线程的引用)来处理。通过这种主动启动线程的方式,避免了掉多线程同时启动,通过CPU切换切换询问状态的方式,节约了CPU的开销。

        a、开启Selector

Selector selector = Selector.open();

      b、注册channel到selector

channel.configureBlocking(false);//设置非阻塞。也就是说不能和FileChannel一起使用了
SelectionKey key=channel.register(selector,SelecotionKey.OP_READ);

 

事件类型 注册类型 类型判断
监听:accept(服务器) SelectionKey.OP_ACCEPT SelctionKey.isAcceptable()
连接:connect(客服端、服务器) SelectionKey.OP_CONNECT SelctionKey.isConnectable()
读:read(客服端、服务器) SelectionKey.OP_READ SelctionKey.isReadable()
写:write(客服端、服务器) SelectionKey.OP_WRITE SelctionKey.isWritable()

                SelectionKey:是channel在selector上的注册标签。当I/O事件准备好的时候,就会返回需要事件类型:

                selectionKey可以获取channel、selector,以及添加和获取附加对象

//这个就是获取channel、处理数据的方式。
Channel channel = selectionKey.channel();
//这个就是获取selector,用来处理完事件后重新注册
Selector selector = selectionKey.selector();
//添加、获取附加对象。
selectionKey.attch(theObject);
Object attachObj = selectionKey.attachment();


          c、从selector中获取事件     (slectionKey可以看作是一个存放channel、附加对象的容,和我们每次注册到selector中需要处理的事件方式。形成了一个映射关系。只要事件达成我们就可以继续处理)    

while(true){
    //第一步:获取事件,只有当有事件处理的时候,selecotr会返回一个大于0的值
    int readyEvents = selector.select();
    if(readyEvents==0) continue;
    //第二步:获取事件标签
    Set<SelectionKey> keys = slector.slectionKeys();
    //第三步:处理事件
    Iterator keyIteraotrs = keys.interator();
    while(keyIterators.hasNext()){
        SelectionKey selectionKey = keyIterators.next();
        //当获取事件的时候,需要从selector删掉。
        keys.remove(selectionKey);
        if(selectionKey.isAcceptable()){
            //do something 。。。
            //注册
        }else if(selectionKey.isConnectable()){
            //do something 。。。
            //注册        
        }else if(selectionKey.isReadable()){
            //do something 。。。
            //注册    
        }else if(selectionKey.isWritable()){
            //do something 。。。
            //注册         
        }
    }
}

        a、文件4、使用       

  try {
            RandomAccessFile fromFile = new RandomAccessFile("D:/project/test/nio/1.txt", "rw");
            FileChannel fromFileChannel = fromFile.getChannel();
            RandomAccessFile toFile = new RandomAccessFile("D:/project/test/nio/2.txt", "rw");
            FileChannel toFileChannel = toFile.getChannel();
            //不同channel的数据传送
            toFileChannel.transferFrom(fromFileChannel, 0, fromFileChannel.size());
            // buffer
//          ByteBuffer buf = ByteBuffer.allocate(48);
//          while (fromFileChannel.read(buf) != -1) {
//              // buf.flip();// 写模式切换成读模式
//              while (buf.hasRemaining()) {
//                  System.out.println(buf.getChar());
//              }
//              // buf.clear();// 从写切换到读
//          }
            fromFile.close();
            toFile.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

   b、serverSocket

// 1.开启Selector
        Selector selector = Selector.open();
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        // 2、设置channel的模式(阻塞-false、非阻塞-true)
        serverSocketChannel.socket().bind(new InetSocketAddress(80));
        serverSocketChannel.configureBlocking(false);
        // 2、注册channel到Selector
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        while (true) {
            int readyChannel = selector.select();
            if (readyChannel == 0)
                continue;
            Set<SelectionKey> keys = selector.selectedKeys();
            Iterator<SelectionKey> keyIterators = keys.iterator();
            while (keyIterators.hasNext()) {
                SelectionKey selectionKey = keyIterators.next();
                keys.remove(selectionKey);
                SocketChannel socketChannel = null;
                if (selectionKey.isAcceptable()) {
                    // 访问事件(这里需要获取的serverSocketChannel)
                    serverSocketChannel = (ServerSocketChannel) selectionKey.channel();
                    socketChannel = serverSocketChannel.accept();
                    socketChannel.configureBlocking(false);
                    socketChannel.register(selector,
                            SelectionKey.OP_WRITE | SelectionKey.OP_READ | SelectionKey.OP_CONNECT);
                } else if (selectionKey.isConnectable()) {
                    socketChannel = (SocketChannel) selectionKey.channel();
                    socketChannel.configureBlocking(false);
                    socketChannel.register(selector, SelectionKey.OP_WRITE | SelectionKey.OP_READ);
                } else if (selectionKey.isReadable()) {
                    socketChannel = (SocketChannel) selectionKey.channel();
                    socketChannel.configureBlocking(false);
                    ByteBuffer buff = ByteBuffer.allocateDirect(1024);
                    while (socketChannel.read(buff) != -1) {
                        buff.flip();
                        while (buff.hasRemaining()) {
                            System.out.println(buff.getChar());
                        }
                        buff.clear();
                    }
                    buff=null;
                    socketChannel.register(selector, SelectionKey.OP_ACCEPT | SelectionKey.OP_WRITE);
                } else if (selectionKey.isWritable()) {
                    socketChannel = (SocketChannel) selectionKey.channel();
                    socketChannel.configureBlocking(false);
                    ByteBuffer buff = ByteBuffer.allocateDirect(1024);
                    buff.put(new String("hello , i am Nio !").getBytes());
                    buff.flip();
                    socketChannel.write(buff);
                    buff=null;
                    socketChannel.register(selector, SelectionKey.OP_ACCEPT | SelectionKey.OP_READ);
                }
            }
        }


       c、socket

// 1.开启Selector
        Selector selector = Selector.open();
        SocketChannel socketChannel = SocketChannel.open();
        // 2、设置channel的模式(阻塞-false、非阻塞-true)
        socketChannel.configureBlocking(false);
        socketChannel.connect(new InetSocketAddress("http://localhost/", 80));
        // 2、注册channel到Selector
        socketChannel.register(selector, SelectionKey.OP_CONNECT);
        while (true) {
 
            // 返回事件
            int readyChannels = selector.select();
            if (readyChannels == 0)
                continue;
            // 有事件发生
 
            // 返回的key集合(返回的是一个channel集合)
            Set<SelectionKey> keys = selector.selectedKeys();
 
            // 对每一个channel进行处理
            Iterator<SelectionKey> keyIterators = keys.iterator();
            while (keyIterators.hasNext()) {
                SelectionKey selectionKey = keyIterators.next();
                keys.remove(selectionKey);
                // 连接发生了
                if (selectionKey.isConnectable()) {
                    // 需要将key(channel)移除、因为selector是条件触发,如果不删除。下次事件来了会发生问题
                    // 从key中获取channel
                    socketChannel = (SocketChannel) selectionKey.channel();
                    socketChannel.configureBlocking(false);
                    // 重新注册(前面删除了注册)
                    socketChannel.register(selector, SelectionKey.OP_WRITE | SelectionKey.OP_READ);
                } else if (selectionKey.isReadable()) {
                    // 读取数据
                    socketChannel = (SocketChannel) selectionKey.channel();
                    socketChannel.configureBlocking(false);
                    ByteBuffer buff = ByteBuffer.allocateDirect(1024);
                    while (socketChannel.read(buff) != -1) {
                        buff.flip();
                        while (buff.hasRemaining()) {
                            System.out.println(buff.getChar());
                        }
                        buff.clear();
                    }
                    buff=null;
                    socketChannel.register(selector, SelectionKey.OP_ACCEPT | SelectionKey.OP_WRITE);
                } else if (selectionKey.isWritable()) {
                    // 写入数据
                    socketChannel = (SocketChannel) selectionKey.channel();
                    socketChannel.configureBlocking(false);
                    ByteBuffer buff = ByteBuffer.allocateDirect(1024);
                    buff.put(new String("hello , i am Nio !").getBytes());
                    buff.blip();
                    socketChannel.write(buff);
                    buff=null;
                    socketChannel.register(selector, SelectionKey.OP_ACCEPT | SelectionKey.OP_READ);
                }
            }
        }


       d、Pipe:两个线程之间的数据传送。传送用sink通道、接受用source通道


 

public void writeToPipeChannel() throws IOException {
        Pipe pipe = Pipe.open();
        Pipe.SinkChannel sinkChannel = pipe.sink();
 
        String newData = "New String to write to file ... " + System.currentTimeMillis();
        ByteBuffer buf = ByteBuffer.allocate(48);
        buf.clear();
        buf.put(newData.getBytes());
        buf.flip();
        while (buf.hasRemaining()) {
            sinkChannel.write(buf);
        }
    }
 
    public void readFromPipeChannel() throws IOException {
        Pipe pipe = Pipe.open();
        Pipe.SourceChannel sourceChannel = pipe.source();
 
        ByteBuffer buf = ByteBuffer.allocateDirect(48);
        buf.clear();
        while (sourceChannel.read(buf) != -1) {
            buf.flip();
            while (buf.hasRemaining()) {
                System.out.println(buf.getChar());
            }
            buf.clear();
        }
    }

    Pipe原理图示

本文转载自:http://my.oschina.net/u/2246410/blog/650578

共有 人打赏支持
清尘V
粉丝 43
博文 107
码字总数 47780
作品 0
青岛
程序员
Java NIO AsynchronousFileChannel

原文链接 , 原文作者:Jakob Jenkov, 翻译:Neil Hao 在Java 7,AsynchronousFileChannel 被添加到了Java NIO中。使用AsynchronousFileChannel可以实现异步地读取和写入文件数据。 创建一个A...

Neil_Hao ⋅ 01/20 ⋅ 0

Java 使用 happen-before 规则实现共享变量的同步操作

前言 熟悉 Java 并发编程的都知道,JMM(Java 内存模型) 中的 happen-before(简称 hb)规则,该规则定义了 Java 多线程操作的有序性和可见性,防止了编译器重排序对程序结果的影响。按照官方的...

stateIs0 ⋅ 01/20 ⋅ 0

Jenkins 教程(一)实现自动化打包及邮件通知

个人不喜欢装腔作势一堆专业术语放上去,让大多数人看不懂来提升逼格(所谓的专家),所以我简单的介绍jenkins是干啥的。本文使用jenkins,就是让它把git仓库里的东西取出来,然后在jenkins容器...

FantJ ⋅ 05/26 ⋅ 0

培训云计算学校,虚拟机基本结构讲解

我们要对JVM虚拟机的结构有一个感性的认知。毕竟我们不是编程人员,认知程度达不到那么深入。一个运行时的Java虚拟机实例的天职是:负责运行一个java程序。当启动一个Java程序时,一个虚拟机...

长沙千锋 ⋅ 05/17 ⋅ 0

阿里巴巴菜鸟Java一面11个问题,你会几个呢?

近日,w3cschool app开发者头条上分享了阿里菜鸟Java程序员一些面试题。 这吸引了不少程序员小伙伴们的注意。 在分享阿里菜鸟Java程序员面经前,来看下Java面试一些面试经验分享: 0、Java高...

W3Cschool ⋅ 04/03 ⋅ 0

Java NIO之Selector(选择器)

历史回顾: Java NIO 概览 Java NIO 之 Buffer(缓冲区) Java NIO 之 Channel(通道) 其他高赞文章: 面试中关于Redis的问题看这篇就够了 一文轻松搞懂redis集群原理及搭建与使用 超详细的Jav...

山川_84b6 ⋅ 05/16 ⋅ 0

Java NIO 系列教程 -- delete

(一) Java NIO 概述 Java NIO 由以下几个核心部分组成: Channels Buffers Selectors 虽然Java NIO 中除此之外还有很多类和组件,但在我看来,Channel,Buffer 和 Selector 构成了核心的A...

数据之美 ⋅ 2013/06/09 ⋅ 4

Java NIO 之 Channel(通道)

历史回顾: Java NIO 概览 Java NIO 之 Buffer(缓冲区) 其他高赞文章: 面试中关于Redis的问题看这篇就够了 一文轻松搞懂redis集群原理及搭建与使用 一 Channel(通道)介绍 通常来说NIO中的所...

山川_84b6 ⋅ 05/15 ⋅ 0

Java 5 、6、 7中新特性

JDK5新特性(与1.4相比)【转】 1 循环 for (type variable : array){ body} for (type variable : arrayList){body} 而1.4必须是: for (int i = 0; i < array.length; i++){ type variabl......

thinkyoung ⋅ 2014/10/14 ⋅ 0

面试必看!2018年4月份阿里最新的java程序员面试题目

目录 技术一面(23问) 技术二面(3大块) 性能优化(21点) 项目实战(34块) JAVA方向技术考察点(15点) JAVA开发技术面试中可能问到的问题(17问) 阿里技术面试1 1.Java IO流的层次结构...

美的让人心动 ⋅ 04/16 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

istio 文档

https://istio.io/docs/concepts/ https://istio.io/docs/concepts/traffic-management/handling-failures/ https://istio.io/docs/concepts/traffic-management/rules-configuration/......

xiaomin0322 ⋅ 14分钟前 ⋅ 0

编程语言的作用及与操作系统和硬件的关系

一、编程语言的作用及与操作系统和硬件的关系 作用:编程语言是计算机语言,是一种程序员与计算机之间沟通的介质,通过编程语言可以使得计算机能够根据人的指令一步一步去工作,完成某种特定...

slagga ⋅ 25分钟前 ⋅ 0

runtime实现按钮点击事件

也不能说是实现吧,,,就是有点类似于RAC里边的写法,不用给btn添加另外的点击事件,就那个add...select...这样子很不友好,来看下代码: [self.btn handleControlEvent:UIControlEventTou...

RainOrz ⋅ 25分钟前 ⋅ 0

Windows系统运维转linux系统运维的经历

开篇之前,首先介绍一下我的背景把:我是一个三线城市的甲方运维。最近,在《Linux就该这么学》书籍的影响下和朋友小A(Linux运维已经三年了,工资也比我的高很多)的影响下,决定转行。最近...

linux-tao ⋅ 26分钟前 ⋅ 0

zip压缩工具,tar打包工具

zip压缩工具 zip打包工具跟前面说到的gzip,bz2,xz 工具最大的不一样是zip可以压缩目录。如果没有安装,需要使用yum install -y zip 来安装。安装完之后就可以直接使用了,跟之前提到的压缩...

李超小牛子 ⋅ 34分钟前 ⋅ 0

使用npm发布自己的npm组件包

一、注册npm账号 官网:https://www.npmjs.com/signup 注册之后需要进行邮箱验证,否则后面进行组件包发布时候会提示403错误,让进行邮箱核准。 二、本地新建一个文件夹,cd进入后使用npm i...

灰白发 ⋅ 36分钟前 ⋅ 0

010. 深入JVM学习—垃圾收集策略概览

1. 新生代可用GC策略 1. 串行GC(Serial Copying) 算法:复制(Copying)清理算法; 操作步骤: 扫描年轻代中所有存活的对象; 使用Minor GC进行垃圾回收,同时将存活对象保存到“S0”或“S...

影狼 ⋅ 36分钟前 ⋅ 0

JVM性能调优实践——JVM篇

在遇到实际性能问题时,除了关注系统性能指标。还要结合应用程序的系统的日志、堆栈信息、GClog、threaddump等数据进行问题分析和定位。关于性能指标分析可以参考前一篇JVM性能调优实践——性...

Java小铺 ⋅ 37分钟前 ⋅ 0

误关了gitlab sign-in 功能的恢复记录

本想关sign-up的,误点了sign-in 退出后登录界面提示: No authentication methods configured 一脸懵逼.. 百度后众多方案说修改application_settings 的 signin_enabled字段; 实际上新版本字段...

铂金蛋蛋 ⋅ 38分钟前 ⋅ 0

登录后,后续请求接口没有带登录cookie可能原因

1.XMLHttpRequest.withCredentials没设置好,参考https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest/withCredentials...

LM_Mike ⋅ 39分钟前 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部