Java 中的 BIO、NIO和 AIO 理解为是 Java 语言对操作系统的各种 IO 模型的封装。程序员在使用这些 API 的时候,不需要关心操作系统层面的知识,也不需要根据不同操作系统编写不同的代码。只需要使用Java的API就可以了。
在讲 BIO,NIO,AIO 之前先来回顾一下这样几个概念:同步与异步,阻塞与非阻塞。
-
同步与异步
同步:同步就是一个任务的完成需要依赖另外一个任务时,只有等待被依赖的任务完成后,依赖的任务才能算完成。
异步: 异步是不需要等待被依赖的任务完成,只是通知被依赖的任务要完成什么工作,依赖的任务也立即执行,只要自己完成了整个任务就算完成了。 -
阻塞和非阻塞
阻塞: 阻塞就是发起一个请求,某个任务一直等待请求结果返回,也就是当前线程会被挂起,无法从事其他任务,只有当条件就绪才能继续。
非阻塞: 非阻塞就是发起一个请求,该任务 不用一直等着结果返回,可以先去干其他事情。
1.BIO (Blocking I/O)
同步阻塞式IO,数据的读取写入必须阻塞在一个线程内等待其完成。
服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。
服务端(简单写写,大概意思就行)
//1服务端
ServerSocker ServerSocker = new ServerSocker(6379);
new Thread(() -> {
while (true) {
//接收客户端的连接---------->此处阻塞 直到client建立连接
Socker client = ServerSocker.accept();
//获取输入流
InputStream in = client.getInputStream();
//读取数据------->此处阻塞 直到网卡里面有数据时
byte[] bs = in.read();
//获取输出流
OutputStream out = client.getOutputStream();
//写数据--------->此处阻塞 直到网卡能让我们写数据
out.writer(bs);
}
}
客户端(一样的)
//2 客户端
//和服务器建立连接
Socker Socker = new Socker(ip,6379);
new Thread(() -> {
while (true) {
//写数据
//获取输出流
OutputStream out = client,getOutputStream();
//写数据
out.writer(bs);
//读数据
//获取输入流
InputStream in = client.getInputStream();
//读取数据
byte[] bs = in.read();
}
}
BIO通过多线程解决阻塞问题,但是十分消耗线程。
在活动连接数不是特别高(小于单机1000)的情况下,这种模型是比较不错的,可以让每一个连接专注于自己的 I/O 并且编程模型简单,也不用过多考虑系统的过载、限流等问题。
2.NIO
NIO是一种同步非阻塞的I/O模型,在Java 1.4 中引入了NIO框架,对应 java.nio 包,提供了 Channel , Selector,Buffer等抽象。
NIO的主要事件有几个:读就绪、写就绪、有新连接到来。
NIO的设计原理(轮询操作,只使用一个线程解决多个客户端并发的问题)
当缓存区里面没有数据时、或者没有准备好数据时,不阻塞,直接返回0
当缓存区里面没有数据时,或者当缓存区里面可以写数据时,阻塞
但是缓存区里面读取数据的速度与向网卡里面的速度是1GB/s,几乎可忽略
read(){
for(缓存区:所有的缓存区){
if(缓存区有数据){
做数据的读取;
}
return 0;
}
}
write(){
for(缓存区:所有的缓存区){
if(缓存区有数据){
做数据的写入;
}
return 0;
}
}
NIO考虑的服务端 减少线程
对于客户端而已BIO的效率高点
这就是我的一点体会,不妥之处多多包涵