NIO 基础

46 阅读1分钟

前言

高性能的Java通信,绝对离不开NIO组件,现在主流的技术框架或中间件服务器,都使用了Java NIO组件,譬如Tomcat、Jetty、Netty。 学习和掌握Java NIO组件,已经不是一项加分技能,而是一项必备技能。 不管是面试,还是实际开发,作为Java“攻城狮”(工程师的谐音),都必须掌握NIO的原理和开发实践技能。 本篇文章来简单介绍NIO的三大组件及其示例代码。

Channel

读写数据的双向通道

Buffer

Channel 将数据写入磁盘过程中的内存缓冲区

ByteBuffer 的基本使用

package com.pine.netty;

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

public class TestByteBuffer {
    public static void main(String[] args) {
        try (FileChannel fc = new FileInputStream("data.txt").getChannel()) {
            // 创建 buffer
            ByteBuffer buffer = ByteBuffer.allocate(10);
            while (true) {
                // 将数据从 channel 读出,并写到 buffer
                int len = fc.read(buffer);
                // 数据已读完
                if (len <= 0) {
                    break;
                }
                // 切换到读模式
                buffer.flip();
                while (buffer.hasRemaining()) {
                    byte b = buffer.get();
                    System.out.println((char) b);
                }
                // 切换会写模式
                buffer.clear();
            }


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

ByteBuffer 的空间分配

package com.pine.netty;

import java.nio.ByteBuffer;

public class TestByteBufferAllocate {
    public static void main(String[] args) {
        /**
         * class java.nio.HeapByteBuffer
         *  在 Java 堆内存中,读写效率低,受到 GC 的影响
         */
        System.out.println(ByteBuffer.allocate(16).getClass());
        /**
         * class java.nio.DirectByteBuffer
         *  在直接内存中,读写效率高,不受 GC 的影响,分配的效率低且容易导致内存泄漏
         */
        System.out.println(ByteBuffer.allocateDirect(16).getClass());
    }
}

selector

配合一个线程来管理多个 channel ,获取这些 channel 上发生的事件并交给线程执行

image.png

黏包和半包

package com.pine.netty;

import java.nio.ByteBuffer;
import static com.pine.netty.util.ByteBufferUtil.*;

public class TestByteBufferExam {
    public static void main(String[] args) {
        ByteBuffer buffer = ByteBuffer.allocate(32);
        buffer.put("helloword\nI'mLihua\nho".getBytes());
        split(buffer);
        buffer.put("wareyou\n".getBytes());
        split(buffer);
    }

    public static void split(ByteBuffer buffer) {
        buffer.flip();
        for (int i = 0; i < buffer.limit(); i++) {
            byte curByte = buffer.get(i);
            if (curByte == '\n') {
                int length = i + 1 - buffer.position();
                ByteBuffer buffer1 = ByteBuffer.allocate(length);
                for (int j = 0; j < length; j++) {
                    buffer1.put(buffer.get());
                }
                debugAll(buffer1);
            }
        }
        buffer.compact();
    }
}

结语

本篇文章到这里就结束了,明天更新阻塞与非阻塞的内容,再见。