Java 非阻塞I/O使用方法
发布时间 - 2026-01-11 03:27:36 点击率:次绝大部分知识与实例来自O'REILLY的《Java网络编程》(Java Network Programming,Fourth Edition,by Elliotte Rusty Harold(O'REILLY))。

非阻塞I/O简介
非阻塞I/O(NIO)是处理高并发的一种手段。在高并发的情况下,创建和回收线程以及在线程间切换的开销变得不容忽视,此时就可以使用非阻塞I/O技术。这种技术的核心思想是每次选取一个准备好的连接,尽快地填充这个连接所能管理的尽可能多的数据,然后转向下一个准备好的连接。
利用非阻塞I/O实现的客户端
一般情况下,客户端不会需要处理很高数量的并发连接。事实上,非阻塞I/O主要是为服务器设计的,但它也可以用在客户端上。由于客户端的设计相比服务器容易,因此下面先用客户端来进行简单演示。
首先介绍通道(channel)和缓冲区。非阻塞I/O中使用SocketChannel类创建连接。要获取SocketChannel对象,需要将一个SocketAddress对象(通常会使用它的子类InetSocketAddress)传入它的静态工厂方法open()中。下面为一个示例:
SocketAddress address = new InetSocketAddress("127.0.0.1", 19);
SocketChannel client = SocketChannel.open(address);
open()方法是阻塞的,因此这之后的代码在连接建立之前不会执行。如果连接无法建立,会抛出一个IOException异常。
连接建立之后就需要获取输入和输出。不同于传统的getInputStream()与getOutputStream(),利用通道,你可以直接写入通道本身。不是写入字节数组,而是要写入一个ByteBuffer对象。ByteBuffer对象通过ByteBuffer.allocate(int capacity)获取,capacity为缓冲区大小,单位为字节:
ByteBuffer buffer = ByteBuffer.allocate(74);
获得ByteBuffer对象后,将其传递给SocketChannel对象的read()方法,SocketChannel对象会用从Socket读取的数据填充这个缓冲区。read()方法返回成功读取并储存在缓冲区中的字节数。默认情况下,它会至少读取一个字节,或者返回-1指示数据结束,没有字节可用时阻塞。这与InputStream的行为大致相同。但如果设置成非阻塞模式,没有字节可用时它会立即返回0,不会阻塞。
现在假定缓冲区内已经有了一些数据,之后就需要将它们提取出来。可以使用传统的方式,先将数据写入一个字节数组,之后再写入一个输出流中。这里介绍一种完全基于通道的方法:利用Channels工具类将输出流封装到一个通道中:
WritableByteChannel out = Channels.newChannel(System.out);
上面的代码将System.out封装入一个通道中。这之后就可以进行输出了。ByteBuffer对象在每次输出之前,需要调用一下它的flip()方法,使得通道从开头开始读。在读写完毕后,还需要调用它的clear()方法,重置缓冲区的状态。下面是进行一次数据输出的代码:
buffer.flip(); out.write(buffer); buffer.clear();
实例1:利用非阻塞I/O实现的CharGenerator(字符生成器)客户端
服务器代码:
public static void createCharGeneratorServer(){
try(ServerSocket server = new ServerSocket(19)){
while(true){
try(Socket connection = server.accept()){
OutputStream out = connection.getOutputStream();
int firstPrintableCharacter = 33;
int numberOfPrintableCharacter = 94;
int numberOfCharactersPerLine = 72;
int start = firstPrintableCharacter;
while(true){
for(int i = start ;
i < start + numberOfCharactersPerLine ; i++){
out.write
(firstPrintableCharacter + (i - firstPrintableCharacter) % numberOfPrintableCharacter);
}
out.write('\r');
out.write('\n');
start = firstPrintableCharacter + (start + 1 - firstPrintableCharacter) % numberOfPrintableCharacter;
}
}catch (IOException e) {
e.printStackTrace();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
客户端代码:
try {
SocketAddress address = new InetSocketAddress("127.0.0.1", 19);
SocketChannel client = SocketChannel.open(address);
ByteBuffer buffer = ByteBuffer.allocate(74);
WritableByteChannel out = Channels.newChannel(System.out);
while(client.read(buffer) != -1){
buffer.flip();
out.write(buffer);
buffer.clear();
}
} catch (IOException e) {
e.printStackTrace();
}
输出(无限循环):
]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEF
^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFG
_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGH
`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHI
abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJ
bcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJK
启用非阻塞模式
上面的程序和使用输入/输出流的传统方式并没有太大差别。不过,可以调用ServerSocket的configureBlocking(false)方法将其设置为非阻塞模式。这个模式下,如果没有可用的数据,read()方法会立即返回,这让客户端可以去做其他事情。不过,由于read()方法在读不到数据时会返回0,读取数据的循环需要做一些改动:
while(true){
//这里可以写每次循环都要做的事,无论有没有读到数据
int n = client.read(buffer);
if(n > 0){
buffer.flip();
out.write(buffer);
buffer.clear();
}else if (n == -1) {
//除非服务器故障,否则不会发生
break;
}
}
总结
以上就是本文关于Java 非阻塞I/O使用方法的全部内容,希望对大家有所帮助。欢迎各位参阅:Java网络编程基础篇之单向通信, Java使用代理进行网络连接方法示例等,有什么问题可以随时留言,小编会及时回复大家。感谢大家对本站的支持!
# java
# 非阻塞
# I/O
# java 中同步、异步、阻塞和非阻塞区别详解
# Java之IO流面试题案例讲解
# 详解Java 网络IO编程总结(BIO、NIO、AIO均含完整实例代码)
# Java三种IO模型原理实例详解
# Java网络编程之IO模型阻塞与非阻塞简要分析
# 客户端
# 将其
# 它会
# 就可以
# 网络编程
# 道中
# 情况下
# 有什么
# 你可以
# 子类
# 很高
# 太大
# 要做
# 去做
# 如果没有
# 还需要
# 所能
# 可以使用
# 不容忽视
# 区内
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
香港服务器网站生成指南:免费资源整合与高速稳定配置方案
免费制作统计图的网站有哪些,如何看待现如今年轻人买房难的情况?
Laravel如何处理文件下载请求?(Response示例)
Laravel控制器是什么_Laravel MVC架构中Controller的作用与实践
HTML 中动态设置元素 name 属性的正确语法详解
javascript中的try catch异常捕获机制用法分析
如何用VPS主机快速搭建个人网站?
linux top下的 minerd 木马清除方法
ChatGPT回答中断怎么办 引导AI继续输出完整内容的方法
详解Android中Activity的四大启动模式实验简述
如何生成腾讯云建站专用兑换码?
如何用景安虚拟主机手机版绑定域名建站?
如何快速使用云服务器搭建个人网站?
DeepSeek是免费使用的吗 DeepSeek收费模式与Pro版本功能详解
怎样使用JSON进行数据交换_它有什么限制
国美网站制作流程,国美电器蒸汽鍋怎么用官方网站?
如何在宝塔面板中创建新站点?
微信小程序 scroll-view组件实现列表页实例代码
Laravel如何使用Laravel Vite编译前端_Laravel10以上版本前端静态资源管理【教程】
Laravel如何使用Blade组件和插槽?(Component代码示例)
如何在阿里云虚拟主机上快速搭建个人网站?
微信公众帐号开发教程之图文消息全攻略
为什么要用作用域操作符_php中访问类常量与静态属性的优势【解答】
如何快速建站并高效导出源代码?
佛山企业网站制作公司有哪些,沟通100网上服务官网?
如何用PHP工具快速搭建高效网站?
BootStrap整体框架之基础布局组件
PythonWeb开发入门教程_Flask快速构建Web应用
如何快速生成专业多端适配建站电话?
1688铺货到淘宝怎么操作 1688一键铺货到自己店铺详细步骤
Python自动化办公教程_ExcelWordPDF批量处理案例
canvas 画布在主流浏览器中的尺寸限制详细介绍
PHP 实现电台节目表的智能时间匹配与今日/明日轮播逻辑
奇安信“盘古石”团队突破 iOS 26.1 提权
,在苏州找工作,上哪个网站比较好?
Laravel的HTTP客户端怎么用_Laravel HTTP Client发起API请求教程
简单实现Android文件上传
uc浏览器二维码扫描入口_uc浏览器扫码功能使用地址
EditPlus中的正则表达式 实战(2)
JS实现鼠标移上去显示图片或微信二维码
如何用ChatGPT准备面试 模拟面试问答与职场话术练习教程
深圳网站制作设计招聘,关于服装设计的流行趋势,哪里的资料比较全面?
Laravel Octane如何提升性能_使用Laravel Octane加速你的应用
教学论文网站制作软件有哪些,写论文用什么软件
?
微信小程序 input输入框控件详解及实例(多种示例)
Android okhttputils现在进度显示实例代码
HTML5建模怎么导出为FBX格式_FBX格式兼容性及导出步骤【指南】
Windows10电脑怎么查看硬盘通电时间_Win10使用工具检测磁盘健康
Laravel如何使用Sanctum进行API认证?(SPA实战)
MySQL查询结果复制到新表的方法(更新、插入)

