Java NIO

问题

  • 什么是nio
  • nio Buffer
  • nio Channel(FileChannel,SocketChannel,ServerSocketChannel )
  • nio 多路复用
  • DirectBuffer直接内存

解答

  • 什么是nio
    NIO是为了弥补传统I/O工作模式的不足而研发的,NIO的工具包提出了基于Selector(选择器)、Buffer(缓冲区)、Channel(通道)的新模式;Selector(选择器)、可选择的Channel(通道)和SelectionKey(选择键)配合起来使用,可以实现并发的非阻塞型I/O能力
  • nio 缓存器
    Buffer相当于一个内存数组,对其的写入和读取都是基于内存的,所以效率更高。
    在这里插入图片描述
    Buffer是一个接口,对应有7种Buffer的实现,对应7种数据类型,除了Boolean类型。
    其实核心是最后的 ByteBuffer,前面的一大串类只是包装了一下它而已,我们使用最多的通常也是 ByteBuffer。
    我们应该将 Buffer 理解为一个数组,IntBuffer、CharBuffer、DoubleBuffer 等分别对应 int[]、char[]、double[] 等。
    MappedByteBuffer 用于实现内存映射文件,也不是本文关注的重点。
    我觉得操作 Buffer 和操作数组、类集差不多,只不过大部分时候我们都把它放到了 NIO 的场景里面来使用而已。下面介绍 Buffer 中的几个重要属性和几个重要方法
  • Channel
    Chanel是一个通道,和Buffer配合使用。例如:
public class BufferExampleTest {

    @Test
    public void testBuffer(){
        /*
        capacity: 内存数组的最大容量,一旦初始化后不能修改
        postsition: 当在写入模式的时候,表示当前写入的位置。使用flip切换到读取模式的时候,position重置为0
        limit:写入模式时表示写入的最大量,等于capacity。读取模式表示读取的最大量,等于写入模式的position
         */
        ByteBuffer byteBuffer = ByteBuffer.allocate(10);
        //ByteBuffer数组中每位是一个byte,写入int意味着最大能存128,超过该值会溢出
        byteBuffer.put((byte)2);
        byteBuffer.put((byte)3);
        byteBuffer.put((byte)4);
        for(int i =0 ; i<byteBuffer.array().length;i++){
//            System.out.println(byteBuffer.array()[i]);
        }
        //将写模式切换到读模式
        byteBuffer.flip();
        for(int i =0; i<byteBuffer.limit();i++){
            System.out.println(byteBuffer.get());
        }

    }
    @Test
    public void testFloatBuffer(){
        FloatBuffer floatBuffer = FloatBuffer.allocate(10);
        floatBuffer.put(2.0f);
        floatBuffer.put(200.f);
        floatBuffer.flip();
        while(floatBuffer.hasRemaining()){
            System.out.println(floatBuffer.get());
        }

    }
    @Test
    public void testChannel() throws IOException {
        /*
        Channel:
         */
        RandomAccessFile randomAccessFile = new RandomAccessFile("copy.txt","rw");
        FileChannel fileChannel = randomAccessFile.getChannel();
        ByteBuffer byteBuffer = ByteBuffer.allocate(100);
        int hasRead =  fileChannel.read(byteBuffer);
        while(hasRead != -1){
            byteBuffer.flip();
            while(byteBuffer.hasRemaining()){
                System.out.println((char) byteBuffer.get());
            }
            byteBuffer.clear();
            hasRead = fileChannel.read(byteBuffer);
        }
        randomAccessFile.close();
    }
    @Test
    public void testChannelFile2() throws IOException {
        FileOutputStream fout = new FileOutputStream("name.txt");
        FileChannel channel = fout.getChannel();
        ByteBuffer byteBuffer = ByteBuffer.allocate(100);
        String name  = "My name is Tom";
        for(int i =0; i<name.length();i++){
            byteBuffer.put(name.getBytes()[i]);
        }
        byteBuffer.flip();
        channel.write(byteBuffer);
        channel.close();
        fout.close();
    }
    @Test
    public void testSocketChannel() throws IOException {
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.bind(new InetSocketAddress("127.0.0.1",3333));
        SocketChannel socketChannel = serverSocketChannel.accept();
        ByteBuffer byteBuffer = ByteBuffer.allocate(1000);
        byteBuffer.put("hello this is server".getBytes());
        byteBuffer.flip();
        socketChannel.write(byteBuffer);
//        socketChannel.close();
    }
    @Test
    public void testSocketClientChannel() throws IOException {
        SocketChannel socketChannel = SocketChannel.open();
        socketChannel.connect(new InetSocketAddress("127.0.0.1",3333));
        ByteBuffer byteBuffer = ByteBuffer.allocate(1000);
        socketChannel.read(byteBuffer);
        byteBuffer.flip();
        StringBuffer sb = new StringBuffer();
        while(byteBuffer.hasRemaining()){
            sb.append((char)byteBuffer.get());
        }
        System.out.println("从服务端收到的数据:"+sb.toString());
    }

}
  • DirectBuffer
    DirectBuffer称之为对外内存或者直接内存。区别于堆内存,受系统内存的限制。是在jdk1.4 nio包中加入的
    创建方式:
   public static ByteBuffer allocateDirect(int capacity) {
        return new DirectByteBuffer(capacity);
    }

构造方法源码:

    DirectByteBuffer(int cap) {                   // package-private

        super(-1, 0, cap, cap);
        boolean pa = VM.isDirectMemoryPageAligned();
        int ps = Bits.pageSize();
        long size = Math.max(1L, (long)cap + (pa ? ps : 0));
        Bits.reserveMemory(size, cap);

        long base = 0;
        try {
          //本地方法,调用c语言中的malloc方法申请一块内存
            base = unsafe.allocateMemory(size);
        } catch (OutOfMemoryError x) {
            Bits.unreserveMemory(size, cap);
            throw x;
        }
        unsafe.setMemory(base, size, (byte) 0);
        if (pa && (base % ps != 0)) {
            // Round up to page boundary
            address = base + ps - (base & (ps - 1));
        } else {
            address = base;
        }
        cleaner = Cleaner.create(this, new Deallocator(base, size, cap));
        att = null;



    }

为什么需要堆外内存?
在以前java io的流的处理上,是有一个copy的过程,将HeapBuffer 在内存中的数据拷贝到一块直接内存。因为如果直接使用HeapBuffer进行io读取和写入,当gc的时候,字节数组的地址会改变,无法保证正常的写入或读取。而直接内存的好处就是零拷贝,而且不受gc的影响。很少被回收。

03-15
### Java NIO 教程与文档 Java NIO 是一种基于缓冲区和通道的 I/O 处理方式,相较于传统的 Java IO 更高效且适合处理大规模数据传输。以下是关于 Java NIO 的一些核心概念及其学习资源。 #### 1. **核心组件** Java NIO 主要由以下几个部分组成: - **Buffer**: 数据容器,用于存储不同类型的原始数据[^4]。 - **Channel**: 类似于流的概念,但支持双向读写操作。 - **Selector**: 支持多路复用机制,允许单线程管理多个 Channel。 这些组件共同构成了高效的非阻塞 I/O 模型,适用于网络编程和文件操作场景。 #### 2. **官方文档** Oracle 官方文档提供了详尽的 Java NIONIO.2 API 描述以及使用示例。可以通过访问 Oracle 官网并搜索 "Java SE documentation" 来获取最新的官方文档[^3]。 #### 3. **教程推荐** Baeldung 网站上的教程涵盖了 Java NIO 的基本原理及高级应用案例。例如,“Five ways to maximize Java NIO and NIO.2” 提供了五个重要的功能介绍,包括变更通知器(Change Notifiers),这使得监听事件变得更加简单。 #### 4. **实际应用场景** 通过结合实例代码可以更好地理解如何利用 Java NIO 实现高性能的数据传输。例如,在文件写入方面有多种实现方法,既可以直接采用标准库中的 `Files` 工具类,也可以借助更底层的 Buffer 和 Channel 结构完成复杂任务[^1]。 ```java import java.nio.file.*; import java.io.IOException; public class FileWriteExample { public static void main(String[] args) throws IOException { Path path = Paths.get("example.txt"); String content = "This is a test."; // 使用 Files.write 方法快速写入字符串到文件 Files.write(path, content.getBytes()); } } ``` 上述代码展示了如何利用 `Files.write()` 函数向指定路径下的文件写入一段文本内容。 --- ####
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值