美文网首页
Java NIO那点事

Java NIO那点事

作者: 爱做梦的胖子 | 来源:发表于2017-06-15 19:04 被阅读109次
  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

相关文章

  • Java NIO那点事

    什么是NIO?New Input/Output基于通道和缓冲区的I/O方式可使用Native函数库直接分配堆外内存...

  • nio

    参考文章 Java Nio Java NIO学习笔记 - NIO客户端时序图 Java NIO学习笔记 - NIO...

  • Java Nio 之Buffer

    Java Nio 系列Java Nio 之BufferJava Nio 之直接内存Java Nio 之高级搬砖工(...

  • Java Nio 之高级搬运工(FileChannel)二

    Java Nio 系列Java Nio 之BufferJava Nio 之直接内存Java Nio 之高级搬砖工(...

  • Java Nio 之高级搬砖工(FileChannel) 一

    Java Nio 系列Java Nio 之BufferJava Nio 之直接内存Java Nio 之高级搬砖工(...

  • Java Nio 之直接内存

    Java Nio 系列Java Nio 之BufferJava Nio 之直接内存Java Nio 之高级搬砖工(...

  • Chapter 15 . Java NIO

    阅读原文 Chapter 15 . Java NIO 15.1 Java NIO 简介 Java NIO(New...

  • java NIO

    并发编程网 - Java NIO系列教程 javaeye - Java NIO 系列教程 NIO 入门 Java ...

  • Java学习资源收集

    Java基础核心 Java NIO Java NIO系列教程(并发编程网) 攻破JAVA NIO技术壁垒 Java...

  • java NIO详解

    Java NIO系列教程(1):Java NIO 概述 NIO学习系列:缓冲区内部实现机制 Java NIO系列教...

网友评论

      本文标题:Java NIO那点事

      本文链接:https://www.haomeiwen.com/subject/ghuuqxtx.html