Java NIO那点事

  1. 什么是NIO?

    • New Input/Output
    • 基于通道和缓冲区的I/O方式
    • 可使用Native函数库直接分配堆外内存
    • 通过存储于Java堆的DirectByteBuffer对象直接操作已分配的堆外内存
    • 同步非阻塞模型
      • 同步是指不间断轮询IO是否就绪
        • 核心是Selector
          • Selector代替了线程本身轮询IO事件,避免了阻塞,同时避免了不必要的线程消耗(如线程等待)
      • 非阻塞是指,等待IO过程中,可以同时做其他任务
        • 核心是通道和缓冲区
          • IO就绪时,通过通道写入缓冲区保证IO成功,无需阻塞等待
    • 与传统IO区别
      • 传统IO面向流,NIO面向缓冲区
      • 传统IO是阻塞式的,NIO是非阻塞的
  2. 几个重要概念

    • Buffer 缓冲区
      • ByteBuffer
      • CharBuffer
      • DoubleBuffer
      • FloatBuffer
      • IntBuffer
      • LongBuffer
      • ShortBuffer
    • Channel 通道
      • SocketChannel
      • ServerSocketChannel
      • FileChannel
      • DatagramChannel
    • Selector 选择器
      • 监管IO事件
      • Channel注册到Selector
      • 单线程处理多个Channel
  3. 实例说事(干货)

  • FileChannel
package com.abuge.nio.demo;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

/**
 * Created by huxianyang on 2017/6/15.
 */
public class FileChannelDemo {
    public static void main(String[] args) {
        normalIOReadFile();

        nioReadFile();
    }

    private static void normalIOReadFile() {

        System.out.println("-----------------start to read file by normal IO-----------------");
        try (InputStream in = new BufferedInputStream(new FileInputStream("src/com/abuge/nio/resource/normal_io.txt"))) {
            byte[] buffer = new byte[1024];
            int byteNum = in.read(buffer);
            while (-1 != byteNum) {
                for (int i = 0; i < byteNum; i++) {
                    System.out.print((char) buffer[i]);
                }
                byteNum = in.read(buffer);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("-----------------end to read file by normal IO-----------------");
    }

    private static void nioReadFile() {
        System.out.println("-----------------start to read file by NIO-----------------");

        try(FileChannel fileChannel = new FileInputStream("src/com/abuge/nio/resource/nio.txt").getChannel()) {
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            int byteReadNum = fileChannel.read(buffer);
            System.out.println("read file byte is " + byteReadNum);

            while(-1 != byteReadNum){
                buffer.flip();
                while (buffer.hasRemaining()){
                    System.out.print((char)buffer.get());
                }
                buffer.compact();
                byteReadNum = fileChannel.read(buffer);
            }

        }catch (Exception e){
            e.printStackTrace();
        }
        System.out.println("-----------------end to read file by NIO-----------------");

    }

}

  • SocketChannel
    • 客户端使用NIO,使用非阻塞模式发送数据
    • 服务端使用IO,阻塞接收数据
package com.abuge.nio.demo;

import java.io.IOException;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.concurrent.TimeUnit;

/**
 * Created by huxianyang on 2017/6/16.
 */
public class SocketChannelDemo {
    public static void main(String[] args) {

        sendByNIO();
    }


    private static void sendByNIO() {
        try (SocketChannel socketChannel = SocketChannel.open()) {
            socketChannel.configureBlocking(false);
            System.out.println("------------- start to connect server -------------");
            boolean connect = socketChannel.connect(new InetSocketAddress("127.0.0.1", 8080));
            System.out.println("--------------connect is " + connect + "------------");
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            if (socketChannel.finishConnect()) {
                System.out.println("------------- connect successful -------------");
                int i = 0;
                while (true) {
                    TimeUnit.SECONDS.sleep(1);
                    String info = "I'm " + (i++) + "-th information from client.";

                    buffer.clear();
                    buffer.put(info.getBytes());
                    buffer.flip();

                    while (buffer.hasRemaining()) {
                        System.out.println("client send " + buffer);
                        socketChannel.write(buffer);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


}


package com.abuge.nio.demo;

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;

/**
 * Created by huxianyang on 2017/6/16.
 */
public class ServerSocketDemo {
    public static void main(String[] args){
        acceptByIO();
    }

    private static void acceptByIO() {
        InputStream in = null;
        try (ServerSocket serverSocket = new ServerSocket(8080);) {
            byte[] buffer = new byte[1024];
            int recvMsgSize = 0;
            while (true) {
                System.out.println("------------- start to receive msg -------------");
                Socket clientSocket = serverSocket.accept();
                SocketAddress clientAddress = clientSocket.getRemoteSocketAddress();
                System.out.println("handling client at " + clientAddress);

                in = clientSocket.getInputStream();

                while (-1 != (recvMsgSize = in.read(buffer))) {
                    byte[] tmp = new byte[recvMsgSize];
                    System.arraycopy(buffer, 0, tmp, 0, recvMsgSize);
                    System.out.println(new String(buffer));
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (null != in) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

  • 服务端的NIO写法
    • ServerSocketChannel
    • Selector
package com.abuge.nio.demo;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;

/**
 * Created by huxianyang on 2017/6/19.
 */
public class ServerConnect {
    private static final int PORT = 8090;
    private static final int TIMEOUT = 3000;
    private static final int BUF_SIZE = 1024;

    public static void main(String[] arsg) {

        selector();
    }

    private static void selector() {
        System.out.println("-------------------start to execute select-------------------");

        try (Selector selector = Selector.open(); ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();) {
            serverSocketChannel.socket().bind(new InetSocketAddress(PORT));
            serverSocketChannel.configureBlocking(false);
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);//表示此通道关注的是连接事件,有连接事件时,selector触发通道执行操作


            while (true) {
                if (0 == selector.select(TIMEOUT)) {
                    System.out.println("============there is no channel is ready to execute IO operations============");
                    continue;
                }

                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                int i = 0;
                while (iterator.hasNext()) {
                    SelectionKey key = iterator.next();

                    if (key.isAcceptable()) {//表示此key对应的通道可以接收连接
                        System.out.println("============the key represent channel is acceptable============ " + i);

                        handleAccept(key);//将通道置为了关注读就绪事件
                    }

                    if (key.isReadable()) {//表示此key对应的通道可以接收读数据
                        System.out.println("============the key represent channel is readable============" + (i++));
                        handleRead(key);
                    }

                    if (key.isWritable()) {//表示此key对应的通道可以接收读数据
                        System.out.println("============the key represent channel is writable============");
                        handleWrite(key);
                    }
                    if (key.isConnectable()) {//表示连接就绪,通常用于client端请求建立连接时
                        System.out.println("============the key represent channel is connectable============");
                    }

                    iterator.remove();
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println("-------------------end to execute select-------------------");
    }

    private static void handleWrite(SelectionKey key) throws IOException {
        SocketChannel socketChannel = (SocketChannel) key.channel();
        ByteBuffer buf = (ByteBuffer) key.attachment();
        buf.flip();

        while (buf.hasRemaining()) {
            socketChannel.write(buf);
            System.out.print((char) buf.get());
        }

        buf.compact();
    }

    private static void handleRead(SelectionKey key) throws IOException {
        SocketChannel socketChannel = (SocketChannel) key.channel();
        ByteBuffer buf = (ByteBuffer) key.attachment();
        int bytesRead = socketChannel.read(buf);

        while (0 < bytesRead) {
            buf.flip();
            while (buf.hasRemaining()) {
                System.out.print((char) buf.get());
            }
            System.out.println("====================================");
            buf.clear();
            bytesRead = socketChannel.read(buf);

            if (-1 == bytesRead) {
                socketChannel.close();
            }
        }
    }

    private static void handleAccept(SelectionKey key) throws IOException {
        ServerSocketChannel serverSocketcChannel = (ServerSocketChannel) key.channel();
        SocketChannel socketChannel = serverSocketcChannel.accept();
        socketChannel.configureBlocking(false);
        socketChannel.register(key.selector(), SelectionKey.OP_READ, ByteBuffer.allocate(BUF_SIZE));
    }


}

PS:Idea常用快捷键

  • Ctrl + Q 查看注释
  • Ctrl + N 查找类
  • 双击Shift 查找Everything
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容

  • Java NIO(New IO)是从Java 1.4版本开始引入的一个新的IO API,可以替代标准的Java I...
    JackChen1024阅读 7,533评论 1 143
  • 从三月份找实习到现在,面了一些公司,挂了不少,但最终还是拿到小米、百度、阿里、京东、新浪、CVTE、乐视家的研发岗...
    时芥蓝阅读 42,169评论 11 349
  • 转自 http://www.ibm.com/developerworks/cn/education/java/j-...
    抓兔子的猫阅读 2,272评论 0 22
  • 这两天了解了一下关于NIO方面的知识,网上关于这一块的介绍只是介绍了一下基本用法,没有系统的解释NIO与阻塞、非阻...
    Ruheng阅读 7,119评论 5 48
  • 君子这个词在现在很难听到。本来一个这么好的词,有时也被用得让人不知其好坏。君子是个对人评价很高的词,难道是现代...
    曼头阅读 659评论 0 0