JAVA BIO至NIO演进
主要阐述点:
1、同步/异步 or 阻塞/非阻塞
2、网络模型演进
3、NIO代码示例
| 一、同步/异步 or 阻塞/非阻塞 |
同步/异步:核心点在于是否等待结果返回。同步即调用者必须等到结果才返回,而异步则可立即返回无需等待结果,通过后期异步回调、状态检查等方式得到结果。
阻塞/非阻塞:核心点在于执行线程是否会阻塞。阻塞,例如在读操作中如果内核数据未准备好则会当阻塞读线程;而非阻塞,在内核数据未准备好前读线程无需等待,可以忙里偷闲干别的事情,
但是需要定期检查。
| 二、网络模型演进 |
1、原始版BIO: 单线程监听链接,每次只处理一个链接请求且读写阻塞。缺点:每次只能处理一个请求,读写阻塞。

2、线程版BIO: 单线程监听链接,可同时处理多个请求,每次分配一个线程处理一个链接请求,线程之间读写非阻塞。缺点:线程可能开设过多导致机器瓶颈,线程过多导致cpu上下文切换消耗大,
线程销毁浪费资源,单线程内部读写依然阻塞。

3、线程池版BIO: 单线程监听链接,可同时处理多个请求,每次将链接请求加入线程池工作队列,减少【线程版BIO】线程创建过多问题,线程之间读写非阻塞。缺点:存在客户链接数量限制,
单线程内部读写依然阻塞。

4、JDK4版本NIO:单线程监听链接,可同时处理多个请求,基于事件驱动形式。Selector封装操作系统调用(如linux epoll),每个客户端链接用通道Channel表示,当通道Channel数据准备完毕将触发相应事件。
- 4.1、NIO解决传统BIO痛点问题:
(1)读写阻塞:传统BIO只有当线程读写完成才会返回,否则线程将阻塞等待。而NIO在通道Channel准备完毕之后会由Selector触发事件,线程基于事件完成相应操作。在内核数据未准备好之前,线程
可以忙里偷闲处理其他逻辑,解决BIO阻塞等待内核数据准备的问题。
(2)客户端链接数量限制:BIO利用开设线程解决客户端之间读写非阻塞问题,但单机开设线程数量存在限制(即使开设线程池处理也有上限),而在像QQ聊天室这样需要建立大量长链接但数据量小的场景中难以满足需求。
在NIO中每个客户端链接对应一个SocketChannel,所有通道Channel注册到Selector统一管理。通道Channel相对线程Thread更轻量级,单机即可同时处理大量链接。
- 4.2、网络模型

上图体现了JAVA NIO 中有3个核心概念:
- Channel:与传统BIO的InputStream/OutputStream类似,区别在于Channel为双向通道支持同时读写。
- Buffer:独立数据缓冲区,所有关于Channel的读写操作都需要经过Buffer。
- Selector:将Channel注册到Selector并监听通道事件,是NIO模型中的核心类。
详细概念参考 JAVA NIO Tutorial
| 三、NIO代码示例 |
服务端Server端代码:
public class MyNioServer {
private Selector selector;
private final static int port = 8686;
private final static int BUF_SIZE = 10240;
private static ByteBuffer byteBuffer = ByteBuffer.allocate(BUF_SIZE);
private void initServer() throws IOException {
//创建通道管理器对象selector
this.selector = Selector.open();
//创建一个通道对象channel
ServerSocketChannel channel = ServerSocketChannel.open();
channel.configureBlocking(false);
channel.socket().bind(new InetSocketAddress(port));
channel.register(selector, SelectionKey.OP_ACCEPT);
while (true){
// 这是一个阻塞方法,一直等待直到有数据可读,返回值是key的数量(可以有多个)
selector.select();
// 如果channel有数据了,将生成的key访入keys集合中
Set<SelectionKey> keys = selector.selectedKeys();
// 得到这个keys集合的迭代器
Iterator<SelectionKey> iterator = keys.iterator();
while (iterator.hasNext()){
SelectionKey key = iterator.next();
iterator.remove();
if (key.isAcceptable()){
doAccept(key);
}else if (key.isReadable()){
doRead(key);
}else if (key.isWritable()){
doWrite(key);
}else if (key.isConnectable()){
System.out.println("连接成功!");
}
}
selector.selectedKeys().clear();
}
}
public void doAccept(SelectionKey key) throws IOException {
ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
System.out.println("ServerSocketChannel正在循环监听");
SocketChannel clientChannel = serverChannel.accept();
clientChannel.configureBlocking(false);
clientChannel.register(key.selector(), SelectionKey.OP_READ);
}
public void doRead(SelectionKey key) throws IOException {
SocketChannel clientChannel = (SocketChannel) key.channel();
byteBuffer.clear() ;
int size = clientChannel.read(byteBuffer);
byteBuffer.flip() ;
byte[] data = byteBuffer.array();
String msg = new String(data, 0, size).trim();
System.out.println("从客户端发送过来的消息是:"+msg);
clientChannel.register(selector, SelectionKey.OP_WRITE);
}
public void doWrite(SelectionKey key) throws IOException {
SocketChannel clientChannel = (SocketChannel) key.channel();
byteBuffer.clear();
byteBuffer.put("收到你的请求 给客户端回复消息".getBytes()) ;
byteBuffer.flip() ;
while (byteBuffer.hasRemaining()){
clientChannel.write(byteBuffer);
}
clientChannel.register(selector, SelectionKey.OP_READ);
}
public static void main(String[] args) throws IOException {
MyNioServer myNioServer = new MyNioServer();
myNioServer.initServer();
}
}
MyNioServer.java
客户端Client代码:
public class MyNioClient {
private Selector selector; //创建一个选择器
private final static int port_server = 8686;
private final static int BUF_SIZE = 10240;
private static ByteBuffer byteBuffer = ByteBuffer.allocate(BUF_SIZE);
private void initClient() throws IOException {
this.selector = Selector.open();
SocketChannel clientChannel = SocketChannel.open();
clientChannel.configureBlocking(false);
clientChannel.connect(new InetSocketAddress(port_server));
clientChannel.register(selector, SelectionKey.OP_CONNECT);
Scanner scanner = new Scanner(System.in);
while (true){
selector.select();
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()){
SelectionKey key = iterator.next();
iterator.remove();
if (key.isConnectable()){
doConnect(key);
}else if (key.isWritable()){
doWrite(key, scanner);
}else if (key.isReadable()){
doRead(key);
}
}
selector.selectedKeys().clear();
}
}
public void doConnect(SelectionKey key) throws IOException {
SocketChannel clientChannel = (SocketChannel) key.channel();
if (clientChannel.isConnectionPending()){
clientChannel.finishConnect();
}
System.out.println("已经与服务端建立链接");
clientChannel.register(selector, SelectionKey.OP_WRITE);
}
public void doWrite(SelectionKey key, Scanner scanner) throws IOException {
SocketChannel clientChannel = (SocketChannel) key.channel();
System.out.print("please input message:");
String message = scanner.nextLine();
byteBuffer.clear();
byteBuffer.put(message.getBytes("UTF-8"));
byteBuffer.flip();
while (byteBuffer.hasRemaining()){
clientChannel.write(byteBuffer);
}
clientChannel.register(selector, SelectionKey.OP_READ);
}
public void doRead(SelectionKey key) throws IOException {
SocketChannel clientChannel = (SocketChannel) key.channel();
byteBuffer.clear() ;
int size = clientChannel.read(byteBuffer);
byteBuffer.flip() ;
byte[] data = byteBuffer.array();
String msg = new String(data, 0 , size).trim();
System.out.println("服务端发送消息:"+msg);
clientChannel.register(selector, SelectionKey.OP_WRITE);
}
public static void main(String[] args) throws IOException {
MyNioClient myNioClient = new MyNioClient();
myNioClient.initClient();
}
}
MyNioClient.java
交互信息:
==> 客户端 已经与服务端建立链接
please input message:hello
服务端发送消息:收到你的请求 给客户端回复消息
please input message:world
服务端发送消息:收到你的请求 给客户端回复消息
please input message:kitty
服务端发送消息:收到你的请求 给客户端回复消息
please input message: ==> 服务端
ServerSocketChannel正在循环监听
从客户端发送过来的消息是:hello
从客户端发送过来的消息是:world
从客户端发送过来的消息是:kitty
参看链接:深入分析 Java I/O 的工作机制
JAVA BIO至NIO演进的更多相关文章
- [转帖]JAVA BIO与NIO、AIO的区别(这个容易理解)
JAVA BIO与NIO.AIO的区别(这个容易理解) https://blog.csdn.net/ty497122758/article/details/78979302 2018-01-05 11 ...
- Java BIO、NIO与AIO的介绍(学习过程)
Java BIO.NIO与AIO的介绍 因为netty是一个NIO的框架,所以在学习netty的过程中,开始之前.针对于BIO,NIO,AIO进行一个完整的学习. 学习资源分享: Netty学习:ht ...
- Java BIO、NIO、AIO 学习(转)
转自 http://stevex.blog.51cto.com/4300375/1284437 先来个例子理解一下概念,以银行取款为例: 同步 : 自己亲自出马持银行卡到银行取钱(使用同步IO时,Ja ...
- Java BIO、NIO、AIO-------转载
先来个例子理解一下概念,以银行取款为例: 同步 : 自己亲自出马持银行卡到银行取钱(使用同步IO时,Java自己处理IO读写). 异步 : 委托一小弟拿银行卡到银行取钱,然后给你(使用异步IO时,Ja ...
- Java BIO、NIO、AIO 学习
正在学习<大型网站系统与JAVA中间件实践>,发现对BIO.NIO.AIO的概念很模糊,写一篇博客记录下来.先来说个银行取款的例子: 同步 : 自己亲自出马持银行卡到银行取钱(使用同步IO ...
- JAVA BIO与NIO、AIO的区别
IO的方式通常分为几种,同步阻塞的BIO.同步非阻塞的NIO.异步非阻塞的AIO. 一.BIO 在JDK1.4出来之前,我们建立网络连接的时候采用BIO模式,需要先在服务端启动一个ServerSock ...
- Java BIO、NIO、AIO 原理
先来个例子理解一下概念,以银行取款为例: 同步 : 自己亲自出马持银行卡到银行取钱(使用同步IO时,Java自己处理IO读写). 异步 : 委托一小弟拿银行卡到银行取钱,然后给你(使用异步IO时,Ja ...
- Java BIO、NIO、AIO 基础,应用场景
Java对BIO.NIO.AIO的支持: Java BIO : 同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必 ...
- 【转】JAVA BIO与NIO、AIO的区别
Java中IO的模型分为三种,同步阻塞的BIO.同步非阻塞的NIO.异步非阻塞的AIO. BIO[同步阻塞] 在JDK1.4出来之前,我们建立网络连接的时候采用BIO模式,需要先在服务端启动一个Ser ...
随机推荐
- Kafka排队:Apache Kafka作为消息传递系统
1.目标 在这个Apache Kafka教程中,我们将学习Apache Kafka Queuing 的概念 .基本上,Kafka中的排队是传统消息传递的模型之一.所以,让我们首先简要介绍Kafka ...
- day28——C/S与B/S架构、网络通信原理、osi七层协议、UDP、TCP协议、TCP的三次握手与四次挥手
day28 C/S B/S架构 C:client 客户端 B:browse浏览器 S:server 服务端 C/S C/S架构:基于客户端与服务端之间的通信 QQ.游戏.皮皮虾 优点:个性化设 ...
- Ubuntu修改文件权限以及更换文件所有者
参数 -R 用来递归实现更改所有子文件和子目录的权限. 1.利用chmod修改权限: 对Document/目录下的所有子文件与子目录执行相同的权限变更: chmod -R 700 文件名 700是变更 ...
- 为了防止页面重新自动加载,可以给a标签设置href="javascript:void(0);"
<a href="javascript:void(0);"></a> <!--按照格式要求,此处的0不能省略!! 虽然省略看上去也没什么影响.但是当发 ...
- 10分钟用scratch写一个大鱼吃小鱼的小游戏
第一次给张江小朋友教Scratch课程之前,还在担心一些概念能不能向小朋友解释清楚,可上完课发现,我严重低估了小朋友的聪明程度,发现现在的孩子相比较自己8.9岁的时候,简直聪明太多倍了. 所以总结了半 ...
- Springboot 结合百度IORC实现自定义模板图片识别
前言: 首先呢,最近再公司的项目当中遇到这样的一个问题,就是需要识别图片,提取图片当中的关键语句,而且识别的语句当然是人家手写体识别,翻来覆去一想,最终还是决定使用百度的OCR帮助我解决这一项需求 话 ...
- 温故知新-网络基础1-ip地址计算
网络地址换算 1.网络地址分类 A类:0.0.0.0到127.255.255.255 255.0.0.0/8 B类:128.0.0.0到191.255.255.255 255.255.0.0/16 C ...
- Java Web 深入分析(11) JVM 体系结构与工作方式
jvm体系 jvm简介 java virtual machine jvm体系详解 jvm工作机制 虚拟机怎么执行代码 jvm为何基于栈 执行引擎 执行引擎过程 java调用栈 总结
- thinkphp5中使用phpmailer实现发送邮件功能
一.开启SMTP服务(使用php发送邮件需要用到SMTP服务,这里以163邮箱的SMTP服务为例). 1.登录163邮箱,在首页上找到“设置”. 2.选择开启的服务,一般都全选,POP3/SMTP/I ...
- 修改网口速度mii-tool和ethtool
mii-tool # mii-tool -F 100baseTx-FD media: 100baseT4, 100baseTx-FD, 100baseTx-HD, 10baseT-FD, 10base ...