文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

全网最清晰JAVA NIO,看一遍就会

2023-09-01 14:29

关注

目录

目录

1.概述

1.1.计算机的IO模型

1.2.NIO概述

2.buffer

2.1.概述

2.2.API

2.3.代码示例

3.channel

3.1.概述

3.2.API

3.2.1.读写

3.2.2.文件复制

3.2.3.阻塞与非阻塞

4.selector

4.1.概述

4.2.代码示例


1.概述

1.1.计算机的IO模型

在聊IO之前一定要了解计算机的IO模型,因为编程语言的所有和IO有关的API,本质上一定是对于计算机IO模型的抽象。

计算机的IO其实就是在内存中为各个IO设备分配了属于它的一块内存,向这块内存中进行读写即可完成IO。这块内存是位于计算机内存中的内核段中的。

一次完整的IO过程是:

CPU执行IO指令

将内核段中数据读到用户段

用户段中数据交给CPU

CPU的执行结果写回用户段

用户段中存的结果写回内核段

 如果对计算机的内存分段、IO等相关概念不熟悉的同学,可以移步博主的另一篇文章,里面详细介绍了相关内容:

详解零拷贝__BugMan的博客-CSDN博客

1.2.NIO概述

NIO,non-blocking IO,从JDK1.4版本开始引入,其直观的特点就是非阻塞,深入一点来看的话,NIO推出之前的JAVA BIO只是对TCP进行了简单的封装,用户只能对进行简单的IO,而整个计算机底层,在内存中的IO过程是被封装成了黑盒的。NIO对计算机底层的IO过程做了抽象,开放出来了内存粒度的API,让使用者可以更加细粒度的从计算机内存的角度来控制IO。

如果不是很了解BIO的同学可以移步作者的另一篇文章,其中详细讲解了BIO:

JAVA BIO__BugMan的博客-CSDN博客

NIO有三大核心:

  1. channel
  2. buffer
  3. selector

1.buffer:

可以理解为用户段的内存的抽象。

2.channel:

可以理解为用户段和内核段IO区的连接的抽象,当然直接理解为内核段的IO区的抽象其实也可以。

3.selector:

NIO实现非阻塞式IO的核心,其可以基于事件监听的方式,选择准备好的channel,再去其中将数据读到buffer中,然后去操作buffer中的数据。这里要是有点晕,不要紧,后文在selector章节,会详细介绍。

2.buffer

2.1.概述

buffer,理解为用户段中一块内存的抽象即可。

既然是一块内存,那么其本质上就是用来进行数据读写的一个容器,由java.nio包定义,顶级接口为Buffer,定义了一套API用来管理缓 冲区中的数据针对存储不同的数据类型,有不同的buffer:

buffer具有以下几个基本属性:

图示:

2.2.API

2.3.代码示例

由于buffer其实就是一块内存的抽象,是一个数据容器,所以核心其实就是put、get。

此处以byteBuffer为例,其它相同。

byte[] resources = "hello".getBytes();//初始化ByteBuffer byteBuffer = ByteBuffer.allocate(1024);System.out.println(byteBuffer.capacity());System.out.println(byteBuffer.position());System.out.println(byteBuffer.limit());//写byteBuffer.put(resources);System.out.println(byteBuffer.capacity());System.out.println(byteBuffer.position());System.out.println(byteBuffer.limit());//读//不开读模式,读不到任何数据System.out.println(byteBuffer.get());System.out.println(byteBuffer.get());//开起读模式才能读到数据byteBuffer.flip();System.out.println(byteBuffer.get());System.out.println(byteBuffer.get());//读取全部System.out.println(new String(byteBuffer.array()));

3.channel

3.1.概述

Channel,通道,由java.nio.channel包下定义,用来向buffer中读写数据,可以理解为内核段和用户段之间进行数据传输的一条逻辑通道,甚至可以直接理解为内核段中内存的一个抽象。

通道具有以下特性:

Channel是一个顶级父接口,针对需要传输的数据格式的不同分为:

可以从以下地方获取不同的通道:

3.2.API

3.2.1.读写

用channel将buffer中的数据写出来:

       try {            FileOutputStream fos = new FileOutputStream( "nio_channel/data01.txt");            //获取file类型的channel            FileChannel channel=fos.getChannel();            //准备好要写出的内容            ByteBuffer buffer=ByteBuffer.allocate(1024);            buffer.put("helloWorld!".getBytes());            //将buffer切换成读模式            buffer.flip();            //写出            channel.write(buffer);            channel.close();        } catch (Exception e) {            e.printStackTrace();        }

用channel将数据读进buffer:

        try {            FileInputStream is=new FileInputStream("data01.txt");            FileChannel channel = is.getChannel();            ByteBuffer buffer=ByteBuffer.allocate(1024);            //用channel将数据读到buffer中            channel.read(buffer);            System.out.println(new String(buffer.array()));        } catch (IOException e) {            e.printStackTrace();        }

3.2.2.文件复制

在JAVA NIO中有两种方式可以实现文件的复制:

  1. 非零拷贝,即一个通道向buffer中写,另一个通道去buffer中读,数据要走用户段。
  2. 零拷贝,直接从磁盘的一个地方拷贝到磁盘的另一个地方,数据不用走用户段。

这里要是对零拷贝不了解的同学,可以移步博主的另一篇文章,对零拷贝进行了详细讲解:

详解零拷贝__BugMan的博客-CSDN博客

1.非零拷贝:

一个channel向buffer中写,另一个channel去buffer中读。

        try {            File srcFile=new File("data01.txt");            File targetFile=new File("data02.txt");            FileInputStream fis=new FileInputStream(srcFile);            FileOutputStream fos=new FileOutputStream(targetFile);            FileChannel isChannel=fis.getChannel();            FileChannel osChannel=fos.getChannel();            ByteBuffer buffer=ByteBuffer.allocate(1024);            while (true){                //读数据                int flag=isChannel.read(buffer);                if(flag==-1){                    break;                }                //读模式                buffer.flip();                //写数据                osChannel.write(buffer);                //清空buffer                buffer.clear();            }        } catch (IOException e) {            e.printStackTrace();        }

2.零拷贝:

当使用Java NIO进行文件传输时,提供了两个底层使用零拷贝的API,一个是transferTo、一个是transferFrom。可以通过transferTo方法将数据从一个Channel传输到另一个Channel,也可以使用transferFrom方法将数据从一个Channel传输到另一个Channel。

// 定义源文件和目标文件路径String sourceFilePath = "path/to/source/file.txt";String targetFilePath = "path/to/target/file.txt";// 创建源文件和目标文件的RandomAccessFile对象try (RandomAccessFile sourceFile = new RandomAccessFile(sourceFilePath, "r");RandomAccessFile targetFile = new RandomAccessFile(targetFilePath, "rw")) {// 获取源文件和目标文件的FileChannelFileChannel sourceChannel = sourceFile.getChannel();FileChannel targetChannel = targetFile.getChannel();// 使用transferTo()方法将数据从源文件传输到目标文件// 从源文件的位置0开始,传输全部文件内容到目标文件long transferredBytes = sourceChannel.transferTo(0, sourceChannel.size(), targetChannel);System.out.println("文件传输成功,传输了 " + transferredBytes + " 字节数据。");// 使用transferFrom()方法将数据从目标文件传输回源文件// 从目标文件的位置0开始,传输全部文件内容回源文件transferredBytes = targetChannel.transferFrom(sourceChannel, 0, sourceChannel.size());System.out.println("数据回传成功,传输了 " + transferredBytes + " 字节数据。");} catch (IOException e) {  e.printStackTrace();}

3.2.3.阻塞与非阻塞

ServerSocketChannel、SocketChannel支持两种阻塞模式:

默认都是阻塞模式,可以手动设置为非阻塞模式。

代码示例:

//准备bufferByteBuffer buffer = ByteBuffer.allocate(1024);//创建服务器ServerSocketChannel serverSocketChannel=ServerSocketChannel.open();//设置为非阻塞模式//serverSocketChannel.configureBlocking(false);//绑定监听端口serverSocketChannel.bind(new InetSocketAddress(8080));//获取连接,这是一步阻塞操作,阻塞模式下,没读到连接会在这一步阻塞;非阻塞模式下不会阻塞,会直接返回一个nullSocketChannel socketChannel = serverSocketChannel.accept();//设置为非阻塞模式//socketChannel.configureBlocking(false);//读数据,这是一步阻塞操作if(socketChannel!=null) {   //阻塞模式下,没读到连接会在这一步阻塞;非阻塞模式下不会阻塞,会直接返回一个null   socketChannel.read(buffer);}

4.selector

4.1.概述

selector,NIO实现非阻塞式IO的核心,它的功能很简单,就是用事件机制来监听channel,挑选出触发事件的channel。

我们知道,如果线程中有IO操作,IO没有完成,资源没有准备好之前,线程是会进入阻塞状态的。我们可以用单线程起一个selector去监听channel是否准备好数据,将准备好数据的channel挑选出来交给其它线程去处理,这样就不会因为IO资源没准备好导致线程阻塞。

我猜大家看到这里会有一个疑惑,什么时候会有上面描述的这种用线程去并发的处理IO?

网络通信的时候

如果用BIO的方式通信一进来就给一个线程去处理,那么就会有可能因为数据包还没收完,IO等待、阻塞,造成线程阻塞。而用NIO的话就可以用selector挑选出数据包收完的IO出来处理,不会有线程阻塞:

selector的事件监听:

通道的监听事件一共有如下类型:

既可以用常量表示,也可以用数字表示。

4.2.代码示例

以下是用NIO进行非阻塞式网络通信的代码示例。

服务端:

//获取通道ServerSocketChannel serverSocketChannel= ServerSocketChannel.open();//切换为非阻塞模式serverSocketChannel.configureBlocking(false);//绑定连接的端口serverSocketChannel.bind(new InetSocketAddress(9999));//获取选择器Selector selector=Selector.open();//将通道注册到选择器上,并开始指定监听接收事件serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);//轮询监听while(selector.select()>0){    Iterator iterator=selector.selectedKeys().iterator();    while (iterator.hasNext()){        SelectionKey selectionKey=iterator.next();        //判当前socket的事件        //1.接收事件(表示socket接收到了数据)        if(selectionKey.isAcceptable()){            SocketChannel socketChannel=serverSocketChannel.accept();            //切换为非阻塞模式            socketChannel.configureBlocking(false);            //将通道以读就绪的事件重新注册到选择器            socketChannel.register(selector,SelectionKey.OP_READ);        }        //2.读就绪事件        if(selectionKey.isReadable()){            SocketChannel socketChannel=(SocketChannel)selectionKey.channel();            //读取数据            ByteBuffer byteBuffer=ByteBuffer.allocate(1024);            int length=0;            while((length=socketChannel.read(byteBuffer))>0){                byteBuffer.flip();                System.out.println(new String(byteBuffer.array(),0,length));                byteBuffer.clear();            }        }        //事件处理完成,移除事件        iterator.remove();    }}

客户端:

//获取通道SocketChannel socketChannel=SocketChannel.open(new InetSocketAddress("127.0.0.1",9999));//切换成非阻塞模式socketChannel.configureBlocking(false);//分配缓冲区ByteBuffer byteBuffer=ByteBuffer.allocate(1024);//发送数据Scanner scanner=new Scanner(System.in);while(true){    String msg=scanner.nextLine();    byteBuffer.put(msg.getBytes());    byteBuffer.flip();    socketChannel.write(byteBuffer);    byteBuffer.clear();}

来源地址:https://blog.csdn.net/Joker_ZJN/article/details/131852342

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯