栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java中的BIO和NIO区别

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java中的BIO和NIO区别

文章目录

Java中的NIO和BIO

BIO原理NIO原理 select()与epoll()

select()与epoll()、poll的区别select()缺点epoll()优点

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 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万。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/749169.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号