【Nio】01--初始Nio组件
时间:2022-07-25
本文章向大家介绍【Nio】01--初始Nio组件,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
Nio学习第一天,三大组件
BIO 朝NIO的演变过程
只能支持一次的socket服务端
此种情况下,客户端发送一次数据以后,服务器端就会停止
public class OnceSocketTcpServer {
static byte[] buffer = new byte[1024];
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket();
serverSocket.bind(new InetSocketAddress(8080))
Socket socket = serverSocket.accept();
int read = socket.getInputStream().read(buffer);
String result = new String(buffer);
System.out.println(result);
} catch (IOException e) {
e.printStackTrace();
}
}
}
支持客户端多次发送请求
现在可以支持多个请求,但是,由于单线程执行,某个方法执行时间太久会造成程序的阻塞
public class OnceSocketTcpServer {
static byte[] buffer = new byte[1024];
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket();
serverSocket.bind(new InetSocketAddress(8080));
while(true) {
Socket socket = serverSocket.accept();
int read = socket.getInputStream().read(buffer);
String result = new String(buffer);
System.out.println(result);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
现在继续改进,支持多线程, 即伪异步方式
在伪异步的情况下,开启多线程的方式来处理。但是,并发的连接数过大,开启的线程过多。cpu资源占用太多
public class OnceSocketTcpServer {
static byte[] buffer = new byte[1024];
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket();
serverSocket.bind(new InetSocketAddress(8080));
while(true) {
final Socket socket = serverSocket.accept();
int read = socket.getInputStream().read(buffer);
new Thread(new Runnable() {
public void run() {
String result = new String(buffer);
System.out.println(result);
}
}).start();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
由于线程过多,我们采用线程池来处理
即时采用线程池的情况下,频繁的切换线程也会造成cpu资源的消耗和浪费
public class OnceSocketTcpServer {
static byte[] buffer = new byte[1024];
public static void main(String[] args) {
try {
ExecutorService executorService = Executors.newCachedThreadPool();
ServerSocket serverSocket = new ServerSocket();
serverSocket.bind(new InetSocketAddress(8080));
while(true) {
final Socket socket = serverSocket.accept();
int read = socket.getInputStream().read(buffer);
executorService.execute(new Runnable() {
public void run() {
String result = new String(buffer);
System.out.println(result);
}
});
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
什么是NIO
三个概念:
- channel管道
- Selector选择器
- Buffer缓冲区
这里先抛砖引玉,为什么redis是单线程,却能够支持高并发
我们需要提到一个概念,多路IO复用机制,这是什么意思呢?
多路:实际上指的就是多个Tcp连接,即上述的多个管道
IO复用:将多个Tcp连接(管道)统一交给一个Selector选择器进行管理。最后,统一使用buffer将数据写入硬盘。
buffer的作用是什么呢
每次单独写入磁盘,效率会特别底下,所以,将数据汇集的多一些以后一起写入,可以提高效率
Bio和Nio到底有什么区别
Bio
在数据没有内核空间的时候,程序会一直阻塞,这个时候cpu放弃了使用权,不能干其他事情
Nio
不管有没有拿到数据都会立即返回结果,如果返回的结果没有数据,会循环请求数据,如果拿到了数据,程序继续执行。这种情况下程序并不会阻塞
Nio、Selector、Channel、Buffer原理
- Nio: 因为上面已经讲述了,这里就不在赘述了
- Selector: Selector选择器,也可以叫做多路复用器,在单线程的情况下维护多个不同的channel
- Channel:客服端传输的数据都必须经过管道,统一注册到selector中管理
- Buffer:BIO是按照字节来写入,效率低下。 缓冲区,将数据添加到缓冲区中,一次性写入,效率更高
Nio架构流程图
Nio的实现步骤
此种实现方式还有一些缺陷,如果客户端断开连接,需要将该连接从Selector选择器中移除。
当然,此处实现还是颇有问题,当某些客户端已经连接上,即存在于Selector中,但是,一直没有发送消息,如果这样的连接过多,也会造成大量的资源浪费。
public class OnceSocketTcpServer {
static ByteBuffer byteBuffer = ByteBuffer.allocate(512);
static List<SocketChannel> selectors = new ArrayList<>();
public static void main(String[] args) {
try {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(8080));
// 设置为异步接收
serverSocketChannel.configureBlocking(false);
while(true) {
// 接收管道内的数据
SocketChannel socketChannel = serverSocketChannel.accept();
// 如果数据不为空,则讲管道添加到selector选择器中
if (socketChannel != null) {
socketChannel.configureBlocking(false);
selectors.add(socketChannel);
}
// 遍历选择器中的所有管道,看是否已经发送数据
for (SocketChannel sel : selectors) {
int j = sel.read(byteBuffer);
if (j > 0) {
byteBuffer.flip();
byte[] bytes = Arrays.copyOf(byteBuffer.array(), byteBuffer.limit());
System.out.println("已经获取到了数据" + new String(bytes));
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
IO复用机制、信号驱动IO、异步IO的区别
- IO复用机制:将多个Tcp连接加载到管道中,然后,交由选择器管理,接收到消息以后统一写入硬盘
- 信号驱动IO:采用事件驱动的方式,有回调来通知
- 异步IO,即AIO
- 物流行业迎变革,云计算是基础,大数据是关键
- Socket学习总结系列(二) -- CocoaAsyncSocket
- 比特币勒索病毒肆虐,腾讯云安全专家给你支招
- HTML5 直播协议之 WebSocket 和 MSE
- IoC在ASP.NET Web API中的应用
- 跟鹅厂老司机学技术之一:“遇见” Kotlin
- 简单的 H5 视频推流解决方案
- 来腾讯云开发者实验室学习.NET
- 跨域资源共享(CORS)在ASP.NET Web API中是如何实现的?
- 使用腾讯云 GPU 学习深度学习系列之六:物体的识别与定位
- npm5 新版功能特性解析及与 yarn 评测对比
- H5直播避坑指南
- 龙门阵之 DevOps 门外汉须知
- 全面进阶 H5 直播(上)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Android巧用XListView实现万能下拉刷新控件
- Android获取其他应用中的assets资源
- Android自定义动态壁纸开发详解
- VsCode插件koroFileHeader一键添加文件头部注释
- 强大的 Stream API(一)
- python3 配置logging日志类的操作
- 你对CSS权重真的足够了解吗?
- python opencv实现图片缺陷检测(讲解直方图以及相关系数对比法)
- Js 的事件循环(Event Loop)机制以及实例讲解
- 你不知道的js中关于this绑定机制的解析[看完还不懂算我输]
- 解决Django部署设置Debug=False时xadmin后台管理系统样式丢失
- 算法-查找斐波纳契数列中第 N 个数
- 在python中修改.properties文件的操作
- Django Xadmin多对多字段过滤实例
- 算法-姓名去重