3 JDK 常见的包和BIO,NIO,AIO

server/2024/12/26 4:43:30/
JDK常见的包

java.lang:系统基础类
java.io:文件操作相关类,比如文件操作
java.nio:为了完善io包中的功能,提高io性能而写的一个新包
java.net:网络相关的包
java.util:java辅助类,特别是集合类
java.sql:数据库操作类

IO流

按照流的流向分可分为输入流和输出流
按照操作单元分可分为字节流和字符流
按照流的角色分可分为节点流和处理流

java io 流共涉及40多个类,看上去很乱,其实有自己的规则,而且都是相关联的。java io都是从下面的四个基类中派生出来的。
InputStream/Reader:所以输入流的基类。前面是字节输入流,后面是字符输入流
OutputStream/Writer:所有输出流的基类,前面是字节输出流,后面是字符输出流

按照操作方式给相关的类分类,如下图:
在这里插入图片描述
![(https://i-blog.csdnimg.cn/direct/93122a2ecec9491e928e2912de6e17d8.png)

在这里插入图片描述

BIO,NIO,AIO有什么区别

BIO (Blocking I/O):同步阻塞I/O模式,数据的读取写入必须阻塞在一个线程内等待其完成。在活动连接数不是特别高(小于单机1000)的情况下,这种模型是比较不错的,可以让每一个连接专注于自己的 I/O 并且编程模型简单,也不用过多考虑系统的过载、限流等问题。线程池本身就是一个天然的漏斗,可以缓冲一些系统处理不了的连接或请求。但是,当面对十万甚至百万级连接的时候,传统的 BIO 模型是无能为力的。因此,我们需要一种更高效的 I/O 处理模型来应对更高的并发量。

NIO (New I/O):NIO是一种同步非阻塞的I/O模型,在Java 1.4 中引入了NIO框架,对应 java.nio包,提供了 Channel , Selector,Buffer等抽象。NIO中的N可以理解为Non-blocking,不单纯是New。它支持面向缓冲的,基于通道的I/O操作方法。 NIO提供了与传统BIO模型中的 Socket 和ServerSocket 相对应的 SocketChannel 和 ServerSocketChannel 两种不同的套接字通道实现,两种通道都支持阻塞和非阻塞两种模式。阻塞模式使用就像传统中的支持一样,比较简单,但是性能和可靠性都不好;非阻塞模式正好与之相反。对于低负载、低并发的应用程序,可以使用同步阻塞I/O来提升开发速率和更好的维护性;对于高负载、高并发的(网络)应用,应使用 NIO 的非阻塞模式来开发

AIO (Asynchronous I/O):AIO 也就是 NIO 2。在 Java 7 中引入了 NIO 的改进版 NIO 2,它是异步非阻塞的IO模型。异步 IO 是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。AIO 是异步IO的缩写,虽然 NIO 在网络操作中,提供了非阻塞的方法,但是 NIO 的 IO 行为还是同步的。对于 NIO来说,我们的业务线程是在 IO 操作准备好时,得到通知,接着就由这个线程自行进行 IO 操作,IO操作本身是同步的。查阅网上相关资料,我发现就目前来说 AIO 的应用还不是很广泛,Netty之前也尝试使用过 AIO,不过又放弃了。

上面是一些书面的表达,读起来感觉就是一些冰冷的文字。
我们先描述下BIO,当我们开启一个线程执行IO操作的时候,要等这个IO流在对应的通道中读写完成之后,线程才能接着往下执行。我们写一个简单的网络编程的代码如下:

import java.io.;
import java.net.
;
public class BIOServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(8080);
while (true) {
Socket clientSocket = serverSocket.accept();
handleClient(clientSocket);
}
}
private static void handleClient(Socket client) throws IOException {
BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(client.getOutputStream())), true);
String request;
while ((request = in.readLine()) != null) {
out.println("Request received: " + request);
if (request.equals(“END”)) {
break;
}
}
in.close();
out.close();
client.close();
}
}

我们发现这段代码中,一直在等待client发送消息,如果没有发送消息就会一直等待并阻塞下去

我们看下下面基于NIO的代码实现

package com.study.test;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
public class Server {
public static void main(String[] args) throws IOException {
//1. 获取通道
ServerSocketChannel ssChannel = ServerSocketChannel.open();
//2. 切换非阻塞模式
ssChannel.configureBlocking(false);
//3. 绑定连接
ssChannel.bind(new InetSocketAddress(9999));
//4. 获取选择器
Selector selector = Selector.open();
//5. 将通道注册到选择器上, 并且指定“监听接收事件”
ssChannel.register(selector, SelectionKey.OP_ACCEPT);
//6. 轮询式的获取选择器上已经“准备就绪”的事件
// selector.select() 方法会阻塞,直到有客户端连接进来,或者客户端SocketChannel发送数据过来
while (selector.select() > 0) {
System.out.println(“轮一轮”);
//7. 获取当前选择器中所有注册的“选择键(已就绪的监听事件)”
Iterator it = selector.selectedKeys().iterator();
while (it.hasNext()) {
//8. 获取准备“就绪”的是事件
SelectionKey sk = it.next();
//9. 判断具体是什么事件准备就绪
if (sk.isAcceptable()) {
//10. 若“接收就绪”,获取客户端连接
SocketChannel sChannel = ssChannel.accept();
//11. 切换非阻塞模式
sChannel.configureBlocking(false);
//12. 将该通道注册到选择器上
sChannel.register(selector, SelectionKey.OP_READ);
} else if (sk.isReadable()) {
//13. 获取当前选择器上“读就绪”状态的通道
SocketChannel sChannel = (SocketChannel) sk.channel();
//14. 读取数据
ByteBuffer buf = ByteBuffer.allocate(1024);//[pos=0 lim=1024 cap=1024]
int len;//数据长度
while ((len = sChannel.read(buf)) > 0) {//[pos=数据长度 lim=1024 cap=1024]
buf.flip();//[pos=0 lim=数据长度 cap=1024]
System.out.println(new String(buf.array(), 0, len));
buf.clear();
}
}
//15. 取消选择键 SelectionKey
it.remove();
}
}
}
}
##客户端的代码
package com.study.test;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.text.SimpleDateFormat;
import java.util.Scanner;
public class Client {
public static void main(String[] args) throws Exception {
//1. 获取通道
SocketChannel sChannel = SocketChannel.open(new InetSocketAddress(“127.0.0.1”, 9999));
//2. 切换非阻塞模式
sChannel.configureBlocking(false);
//3. 分配指定大小的缓冲区
ByteBuffer buf = ByteBuffer.allocate(1024);
//4. 发送数据给服务端
Scanner scan = new Scanner(System.in);
while (scan.hasNext()) {
String str = scan.nextLine();
buf.put((new SimpleDateFormat(“yyyy/MM/dd HH:mm:ss”).format(System.currentTimeMillis())
+ “\n” + str).getBytes());
buf.flip();
sChannel.write(buf);
buf.clear();
}
//5. 关闭通道
sChannel.close();
}
}
这个案例启动了一个 Selector 选择器,将服务端通道 ServerSocketChannel 注册到 Selector 选择器上,并绑定一个接收 OP_ACCEPT 事件
然后调用 Selector 选择器select()方法,这个方法是阻塞,直到 有 OP_ACCEPT事件(有客户端SocketChannel 连接请求),select()方法不再阻塞,继续往下执行,
接收到客户端SocketChannel 连接请求后,将这个SocketChannel 再注册到 Selector 选择器上,并绑定一个读 OP_READ 事件,以后 选择器 就可以监听到 客户端发送过来的数据
每次 Selector 选择器 接收到 事件驱动后 要记得移除 这个驱动事件

这就是我们NIO的处理方式。如下图:

在这里插入图片描述


http://www.ppmy.cn/server/153226.html

相关文章

go-zero(十五)缓存实践:分页列表

go zero 缓存实践:分页列表 在实际开发中,分页列表 是一个非常常见的需求,尤其在面对大量数据时,通过分页可以有效减轻服务器和数据库的压力,提高用户体验。本篇文章将通过go zero 和 Redis 的结合,提供一…

【python】OpenCV—Image Moments

文章目录 1、功能描述2、图像矩3、代码实现4、效果展示5、完整代码6、涉及到的库函数cv2.moments 7、参考 1、功能描述 计算图像的矩,以质心为例 2、图像矩 什么叫图像的矩,在数字图像处理中有什么作用? - 谢博琛的回答 - 知乎 https://ww…

概率论 期末 笔记

第一章 随机事件及其概率 利用“四大公式”求事件概率 全概率公式与贝叶斯公式 伯努利概型求概率 习题 推导 一维随机变量及其分布 离散型随机变量(R.V)求分布律 利用常见离散型分布求概率 连续型R.V相关计算 利用常见连续型分布的计算 均匀分布 正态…

如何处理对象的创建与销毁的时机?C++ 中面向对象编程如何处理对象的状态存储与恢复?

1) 如何处理对象的创建与销毁的时机? 在C中,对象的创建与销毁是内存管理的重要部分,可以通过多种方式来实现和控制。 对象的创建 静态存储期(Static Storage Duration): 全局对象或静态局部对象在程序开…

React基础知识(总结回顾一)

React知识框架图可以帮助理清React的核心概念及其相关技术。下面是一个大致的React知识框架图,按功能模块将React的核心知识进行分类,包括从基础到进阶的内容。 React 知识框架图: --------------------- | React 基础 | --------…

RabbitMQ 路由(Routing)通讯方式详解

在现代分布式系统中,消息队列(Message Queue)是实现异步通信、解耦系统组件的重要工具。RabbitMQ 作为一个广泛使用的消息代理(Message Broker),提供了多种消息传递模式,其中路由(Ro…

MFC/C++学习系列之简单记录7

MFC/C学习系列之简单记录7 前言句柄的介绍句柄的使用AFX开头的函数都是干什么用的?总结 前言 在MFC的使用中发现了句柄,今天来详细学习一下MFC中如何使用句柄吧! 句柄的介绍 句柄的使用是资源管理和传递的关键机制,通过句柄将系…

在C#中使用反射获取对象的类型信息

在C#中,反射(Reflection)是一种强大的机制,允许在运行时获取有关程序集、模块和类型的元数据,并且能够动态地调用方法和访问字段。通过反射可以获取对象的类型信息,包括其属性、方法、事件等。 以下是如何…