文章目录
- Java中的NIO和BIO
-
- BIO原理
- NIO原理
- select()与epoll()
-
- select()与epoll()、poll的区别
- select()缺点
- epoll()优点
Java中的NIO和BIO 首先我们先了解一下,阻塞(Block)和非阻塞(Non-Block).
阻塞:往往需要等待数据缓冲区的数据准备好以后才处理其它事情,否则一致等待在哪里。
非阻塞:当进程访问我们的数据缓冲区的时候,如果数据没有准备好就立即返回,不会等待。如果数据以及准备好,也直接返回。
阻塞和非阻塞是进程在访问数据缓冲区的时候,数据是否准备就绪的一种处理方式。同步: 在同一时间只能做一件事情。
异步: 在处理数据的时候,同一时间点能做多个处理。
NIO:同步非阻塞IO。BIO原理
BIO:同步阻塞IO。
AIO:异步非阻塞IO。
public class Server { final static int PROT = 8765;
public static void main(String[] args) {ServerSocket server = null;
try {
server = new ServerSocket(PROT);
System.out.println(" server start .. ");
//等待客户端的连接,阻塞方法
//socket对象是数据发送者在服务端的引用
Socket socket = server.accept();
//进行阻塞
//新建一个线程执行客户端的任务
new Thread(new ServerHandler(socket)).start();
//传统的TCP点对点直连接的方式,每一个连接在server端都要创建一个线程;
// windows最大支持1000个线程,unix最大支持2000个线程;
} catch (Exception e) {
e.printStackTrace();
} finally {
if(server != null){
try {
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
server = null;
}
}
BIO会在代码 Socket socket = server.accept(); 处进行阻塞,这个时候正在等待连接和接收数据。
NIO原理
public class Server implements Runnable{
//1 多路复用器(管理所有的通道)
private Selector seletor;
//2 建立缓冲区
private ByteBuffer readBuf = ByteBuffer.allocate(1024);
//3
private ByteBuffer writeBuf = ByteBuffer.allocate(1024);
public Server(int port){
try {
//1 打开路复用器(选择器)
this.seletor = Selector.open();
//2 打开服务器通道
ServerSocketChannel ssc = ServerSocketChannel.open();
//3 设置服务器通道为非阻塞模式
ssc.configureBlocking(false);
//4 绑定地址
ssc.bind(new InetSocketAddress(port));
//5 把服务器通道注册到多路复用器上,并且监听阻塞事件
ssc.register(this.seletor, SelectionKey.OP_ACCEPT);
System.out.println("Server start, port :" + port);
} catch (IOException e) {
e.printStackTrace();
}
}
private void accept(SelectionKey key) {
try {
//1 获取服务通道
ServerSocketChannel ssc =(ServerSocketChannel) key.channel();
//2 执行阻塞方法
SocketChannel sc = ssc.accept();
//3 设置阻塞模式 为非阻塞
sc.configureBlocking(false);
//4 注册读事件(服务端一般不注册 可写事件)
sc.register(this.seletor, SelectionKey.OP_READ);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
while(true){
try {
//1 必须要让多路复用器开始监听(轮询)
this.seletor.select();
//2 返回多路复用器已经选择的结果集
Iterator keys = this.seletor.selectedKeys().iterator();
//3 进行遍历
while(keys.hasNext()){
//4 获取一个选择的元素
SelectionKey key = keys.next();
//5 直接从容器中移除就可以了
keys.remove();
//6 如果是有效的
if(key.isValid()){
//7 如果为阻塞状态
if(key.isAcceptable()){
this.accept(key);
}
//8 如果为可读状态
if(key.isReadable()){
this.read(key);
}
//9 写数据
//if(key.isWritable()){
//this.write(key);
//ssc
//}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void write(SelectionKey key){
//ServerSocketChannel ssc =(ServerSocketChannel) key.channel();
//服务端一般不注册 可写事件
//ssc.register(this.seletor, SelectionKey.OP_WRITE);
}
private void read(SelectionKey key) {
try {
//1 清空缓冲区旧的数据
this.readBuf.clear();
//2 获取之前注册的socket通道对象
SocketChannel sc = (SocketChannel) key.channel();
//3 读取数据
int count = sc.read(this.readBuf);
//4 如果没有数据
if(count == -1){
key.channel().close();
key.cancel();
return;
}
//5 有数据则进行读取 读取之前需要进行复位方法(把position 和limit进行复位)
this.readBuf.flip();
//6 根据缓冲区的数据长度创建相应大小的byte数组,接收缓冲区的数据
byte[] bytes = new byte[this.readBuf.remaining()];
//7 接收缓冲区数据
this.readBuf.get(bytes);
//8 打印结果
String body = new String(bytes).trim();
System.out.println("Server : " + body);
// 9..可以写回给客户端数据
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
new Thread(new Server(8765)).start();
;
}
}
NIO多了一个ByteBuffer缓冲区,所以实现了非阻塞。
NIO底层利用了select()/epoll()。
select()与epoll() 早期1.6版本,NIO底层实现用的是select()。
jdk1.8以后NIO底层用的是epoll()。
select()与epoll()、poll的区别 select():—>O(n)
它仅仅知道有I/O事件发生了,却不知道具体是哪个流。只能做轮训,找出能够读出数据或写入数据的流。
select()具有O(n)的无差别轮训的复杂度。
poll():—>O(n)
poll()本质上和select()没有区别,它将用户传入的数据从用户空间拷贝到内核空间。
相比select()它没有最大连接数限制(底层是由链表实现的)。
epoll():—>时间复杂度O(1)
epoll()会把哪个流发生了I/O事件通知我们,所以epoll()是基于事件驱动的。
1)select()、poll()、epoll()都是I/O多路复用机制,可以监视多个文件描述符。一旦描述符就绪,就能通知程序进行相应的读写操作。
2)select()、poll()、epoll() 本质上都是同步I/O,因为他们都需要在读写事件就绪后,自己负责进行读写。这个读写过程是 ‘‘阻塞的’’。非阻塞指的是,数据流在写入、读取ByteBuffer的时候。
3)AIO(异步非阻塞I/O):无需自己负责进行读写,异步I/O会把数据从内核拷贝到用户空间。
select()缺点 1)单个进程能够监听的文件描述符fd有限,默认是1024。
2)每次调用select都会把描述符fd由内核空间---->用户空间的拷贝。
3)使用轮训的方式,需要传进来所有的fd。
epoll()优点 【JavaWeb开发|Java中的BIO和NIO区别】没有监听的Fd描述符,远大于1024.一般1G内存,默认是10万。
推荐阅读
- 蓝桥杯|2018第九届蓝桥杯国赛JAVA B组真题解析(带源码及解析)
- Java每日一题|【蓝桥Java每日一题】——14.球会落何处(有趣模拟题)
- 蓝桥真题|【蓝桥真题3】蓝桥改革变难,想进国赛这些能力你可缺一不可
- 大数据|什么是研发效能(为什么要关注研发效能)
- 从简单例子深入理解hashMap的put和get原理
- python|python 第7讲 面向对象
- kafka|kafka 基础知识 第一讲
- java idea使用小辣椒的步骤
- maven|Java-Maven详解