IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Java中的BIO和NIO区别 -> 正文阅读

[Java知识库]Java中的BIO和NIO区别

Java中的NIO和BIO

首先我们先了解一下,阻塞(Block)和非阻塞(Non-Block).
阻塞:往往需要等待数据缓冲区的数据准备好以后才处理其它事情,否则一致等待在哪里。
非阻塞:当进程访问我们的数据缓冲区的时候,如果数据没有准备好就立即返回,不会等待。如果数据以及准备好,也直接返回。

阻塞和非阻塞是进程在访问数据缓冲区的时候,数据是否准备就绪的一种处理方式。

同步: 在同一时间只能做一件事情。
异步: 在处理数据的时候,同一时间点能做多个处理。

NIO:同步非阻塞IO。
BIO:同步阻塞IO。
AIO:异步非阻塞IO。

BIO原理

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<SelectionKey> 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()优点

没有监听的Fd描述符,远大于1024.一般1G内存,默认是10万。

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-02-28 15:14:49  更:2022-02-28 15:17:35 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 11:34:23-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码