高并发Java:NIO和AIO(一)

简介: 高并发Java:NIO和AIO

IO感觉上和多线程并没有多大关系,但是NIO改变了线程在应用层面使用的方式,也解决了一些实际的困难。而AIO是异步IO和前面的系列也有点关系。在此,为了学习和记录,也写一篇文章来介绍NIO和AIO。


1. 什么是NIO


NIO是New I/O的简称,与旧式的基于流的I/O方法相对,从名字看,它表示新的一套Java I/O标 准。它是在Java 1.4中被纳入到JDK中的,并具有以下特性:


NIO是基于块(Block)的,它以块为基本单位处理数据 (硬盘上存储的单位也是按Block来存储,这样性能上比基于流的方式要好一些)

为所有的原始类型提供(Buffer)缓存支持

增加通道(Channel)对象,作为新的原始 I/O 抽象

支持锁(我们在平时使用时经常能看到会出现一些.lock的文件,这说明有线程正在使用这把锁,当线程释放锁时,会把这个文件删除掉,这样其他线程才能继续拿到这把锁)和内存映射文件的文件访问接口

提供了基于Selector的异步网络I/O

image.png

所有的从通道中的读写操作,都要经过Buffer,而通道就是io的抽象,通道的另一端就是操纵的文件。


2. Buffer


1.png

Java中Buffer的实现。基本的数据类型都有它对应的Buffer

Buffer的简单使用例子:

package test;
import java.io.File;
import java.io.FileInputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class Test {
    public static void main(String[] args) throws Exception {
       FileInputStream fin = new FileInputStream(new File(
          "d:\\temp_buffer.tmp"));
       FileChannel fc = fin.getChannel();
       ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
       fc.read(byteBuffer);
       fc.close();
       byteBuffer.flip();//读写转换
    }
}

总结下使用的步骤是:

  1. 得到Channel
  2. 申请Buffer
  3. 建立Channel和Buffer的读/写关系
  4. 关闭

下面的例子是使用NIO来复制文件:

public static void nioCopyFile(String resource, String destination)
         throws IOException {
       FileInputStream fis = new FileInputStream(resource);
       FileOutputStream fos = new FileOutputStream(destination);
       FileChannel readChannel = fis.getChannel(); // 读文件通道
       FileChannel writeChannel = fos.getChannel(); // 写文件通道
       ByteBuffer buffer = ByteBuffer.allocate(1024); // 读入数据缓存
       while (true) {
         buffer.clear();
         int len = readChannel.read(buffer); // 读入数据
         if (len == -1) {
          break; // 读取完毕
         }
         buffer.flip();
         writeChannel.write(buffer); // 写入文件
       }
       readChannel.close();
       writeChannel.close();
    }

Buffer中有3个重要的参数:位置(position)、容量(capactiy)和上限(limit)

1.png

这里要区别下容量和上限,比如一个Buffer有10KB,那么10KB就是容量,我将5KB的文件读到Buffer中,那么上限就是5KB。

下面举个例子来理解下这3个重要的参数:

public static void main(String[] args) throws Exception {
       ByteBuffer b = ByteBuffer.allocate(15); // 15个字节大小的缓冲区
       System.out.println("limit=" + b.limit() + " capacity=" + b.capacity()
          + " position=" + b.position());
       for (int i = 0; i < 10; i++) {
         // 存入10个字节数据
         b.put((byte) i);
       }
       System.out.println("limit=" + b.limit() + " capacity=" + b.capacity()
          + " position=" + b.position());
       b.flip(); // 重置position
       System.out.println("limit=" + b.limit() + " capacity=" + b.capacity()
          + " position=" + b.position());
       for (int i = 0; i < 5; i++) {
         System.out.print(b.get());
       }
       System.out.println();
       System.out.println("limit=" + b.limit() + " capacity=" + b.capacity()
          + " position=" + b.position());
       b.flip();
       System.out.println("limit=" + b.limit() + " capacity=" + b.capacity()
          + " position=" + b.position());
    }

整个过程如图:

1.png

此时position从0到10,capactiy和limit不变。

1.png

该操作会重置position,通常,将buffer从写模式转换为读 模式时需要执行此方法 flip()操作不仅重置了当前的position为0,还将limit设置到当前position的位置 。


limit的意义在于,来确定哪些数据是有意义的,换句话说,从position到limit之间的数据才是有意义的数据,因为是上次操作的数据。所以flip操作往往是读写转换的意思。

image.png

意义同上。

而Buffer中大多数的方法都是去改变这3个参数来达到某些功能的:

public final Buffer rewind()

将position置零,并清除标志位(mark)

public final Buffer clear()

将position置零,同时将limit设置为capacity的大小,并清除了标志mark
1.png

public final Buffer flip()

先将limit设置到position所在位置,然后将position置零,并清除标志位mark,通常在读写转换时使用


文件映射到内存


public static void main(String[] args) throws Exception {
       RandomAccessFile raf = new RandomAccessFile("C:\\mapfile.txt", "rw");
       FileChannel fc = raf.getChannel();
       // 将文件映射到内存中
       MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_WRITE, 0,
          raf.length());
       while (mbb.hasRemaining()) {
         System.out.print((char) mbb.get());
       }
       mbb.put(0, (byte) 98); // 修改文件
       raf.close();
    }

对MappedByteBuffer的修改就相当于修改文件本身,这样操作的速度是很快的。

相关文章
|
3天前
|
缓存 Java UED
BIO、NIO、AIO有什么区别
【8月更文挑战第16天】BIO、NIO、AIO有什么区别
15 4
|
1天前
|
Java
"揭秘Java IO三大模式:BIO、NIO、AIO背后的秘密!为何AIO成为高并发时代的宠儿,你的选择对了吗?"
【8月更文挑战第19天】在Java的IO编程中,BIO、NIO与AIO代表了三种不同的IO处理机制。BIO采用同步阻塞模型,每个连接需单独线程处理,适用于连接少且稳定的场景。NIO引入了非阻塞性质,利用Channel、Buffer与Selector实现多路复用,提升了效率与吞吐量。AIO则是真正的异步IO,在JDK 7中引入,通过回调或Future机制在IO操作完成后通知应用,适合高并发场景。选择合适的模型对构建高效网络应用至关重要。
|
20天前
|
安全 Java Linux
(七)Java网络编程-IO模型篇之从BIO、NIO、AIO到内核select、epoll剖析!
IO(Input/Output)方面的基本知识,相信大家都不陌生,毕竟这也是在学习编程基础时就已经接触过的内容,但最初的IO教学大多数是停留在最基本的BIO,而并未对于NIO、AIO、多路复用等的高级内容进行详细讲述,但这些却是大部分高性能技术的底层核心,因此本文则准备围绕着IO知识进行展开。
|
30天前
|
算法 Java 调度
高并发架构设计三大利器:缓存、限流和降级问题之使用Java代码实现令牌桶算法问题如何解决
高并发架构设计三大利器:缓存、限流和降级问题之使用Java代码实现令牌桶算法问题如何解决
|
1月前
|
监控 网络协议 Java
Java面试题:解释Java NIO与BIO的区别,以及NIO的优势和应用场景。如何在高并发应用中实现NIO?
Java面试题:解释Java NIO与BIO的区别,以及NIO的优势和应用场景。如何在高并发应用中实现NIO?
29 0
|
1月前
|
Java 大数据
解析Java中的NIO与传统IO的区别与应用
解析Java中的NIO与传统IO的区别与应用
|
3月前
|
存储 监控 Java
深入探索Java语言的NIO(New I/O)技术
深入探索Java语言的NIO(New I/O)技术
|
1月前
|
Java
Java中的NIO编程详解
Java中的NIO编程详解
|
1月前
|
Java 大数据
如何在Java中进行网络编程:Socket与NIO
如何在Java中进行网络编程:Socket与NIO
|
1月前
|
Java
Java中的NIO编程详解
Java中的NIO编程详解