如何解读-Java-IO、NIO-中的同步阻塞与同步非阻塞?

本文详细对比了Java传统IO中的同步阻塞机制(如ServerSocket的accept()和Socket的read()),以及JavaNIO中的非阻塞I/O,介绍了NIO中的Selector和多路复用。讨论了NIO的同步非阻塞特性及其优点,并提到了Netty框架在NIO编程中的重要性。
摘要由CSDN通过智能技术生成

}
}).start();
}
}

在 IOServer 中,会开着 while 死循环一直调用 ServerSocket#accpet() 方法来监听等待客户端连接:ServerSocket 主动监听是否有客户端请求连接,如果没有的话就会一直阻塞等待着,所以说 IO 是同步阻塞的;

当 ServerSocket 接收到新的连接请求,一般会创建一条新线程来处理接下来客户端的写请求(当然了,也可以在同一条线程中处理);在线程里面,会调用 Socket 输入流(InputStream)的 read(byte b[]) 方法来读取客户端发送过来的数据:该方法会一直阻塞着,直到客户端发送数据过来;当发现内核态中有数据了,就会将数据复制到用户态中(也就是字节数组中),所以说 IO 是同步阻塞的。

弊端:当消息发送方发送请求比较缓慢,或者网络传输比较慢时,消息接收方的读取输入流会被长时间堵塞,直到发送方的数据发送完成。

2、接下来继续看看 Java IO 编程中的客户端代码:

public class IOClient {

public static void main(String[] args) {
new Thread(() -> {
try {
Socket socket = new Socket(“127.0.0.1”, 8000);
while (true) {
try {
socket.getOutputStream().write((new Date() + “: hello world”).getBytes());
Thread.sleep(2000);
} catch (Exception e) {
}
}
} catch (IOException e) {
}
}).start();
}
}

在 IOClient 中,开着 while 死循环一直调用客户端 Socket 输出流(OutputStream)的 write(byte b[]) 方法往服务端发送数据;而此时,客户端会一直阻塞着,直到所有的字节全部写入完毕或者发生异常。

弊端:当消息接收方处理比较缓慢时,最后可能会导致 TCP 的缓冲区充满未被处理的数据;此时消息发送方不能再继续往 TCP 缓冲区写入消息,会一直被阻塞着。

3、Java IO 同步阻塞解读:

在 Java IO 中,不管是服务端还是客户端,不管是读取数据还是写入数据,都需要自己主动去完成这个 I/O 操作,这就是同步。而如果对方处理消息的效率比较慢,进程可能会因为执行此次 I/O 操作而导致被一直阻塞着,这就是阻塞。

Java NIO 编程:

1、我们先看看 Java NIO 编程中的服务端代码:

public class NIOServer {
public static void main(String[] args) throws IOException {
Selector serverSelector = Selector.open();
Selector clientSelector = Selector.open();

new Thread(() -> {
try {
// 对应IO编程中服务端启动
ServerSocketChannel listenerChannel = ServerSocketChannel.open();
listenerChannel.socket().bind(new InetSocketAddress(8000));
listenerChannel.configureBlocking(false);
listenerChannel.register(serverSelector, SelectionKey.OP_ACCEPT);

while (true) {
// 监测是否有新的连接,这里的1指的是阻塞的时间为 1ms
if (serverSelector.select(1) > 0) {
Set set = serverSelector.selectedKeys();
Iterator keyIterator = set.iterator();

while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();

if (key.isAcceptable()) {
try {
// (1) 每来一个新连接,不需要创建一个线程,而是直接注册到clientSelector
SocketChannel clientChannel = ((ServerSocketChannel) key.channel()).accept();
clientChannel.configureBlocking(false);
clientChannel.register(clientSelector, SelectionKey.OP_READ);
} finally {
keyIterator.remove();
}
}

}
}
}
} catch (IOException ignored) {
}

}).start();

new Thread(() -> {
try {
while (true) {
// (2) 批量轮询是否有哪些连接有数据可读,这里的1指的是阻塞的时间为 1ms
if (clientSelector.select(1) > 0) {
Set set = clientSelector.selectedKeys();
Iterator keyIterator = set.iterator();

while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();

if (key.isReadable()) {
try {
SocketChannel clientChannel = (SocketChannel) key.channel();
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
// (3) 面向 Buffer
clientChannel.read(byteBuffer);
byteBuffer.flip();
System.out.println(Charset.defaultCharset().newDecoder().decode(byteBuffer)
.toString());
} finally {
keyIterator.remove();
key.interestOps(SelectionKey.OP_READ);
}
}

}
}
}
} catch (IOException ignored) {
}
}).start();

}
}

在 NIOServer 中,会创建并打开两个 Selector ,Selecotr 是 Java NIO 中的核心组件,底层利用的是 I/O 多路复用模型。

  • 一个 Selector 负责监听 ServerSocketChannel 中的客户端连接请求,如果有新的客户端请求连接,那么就会创建对应的 SocketChannel,然后往另外一个 Selector 中注册;如果没有,则直接返回,不会在这里阻塞着,进程可以继续做别的事情,所以 NIO 是同步非阻塞。

  • 第二个 Selector,就是负责监听哪些 SocketChannel 有读写事件,如果有的话则进行对应的 I/O 操作;而如果没有,也是直接返回,不会在这里一直阻塞着,进程可以继续做别的事情,所以 NIO 是同步非阻塞。

2、Java NIO 中客户端的编程:

这个我们就不用上代码了,其实和服务端中第二个 Selector 的使用一样的。

3、Java NIO 同步非阻塞解读:

在 Java NIO 中,不管是服务端还是客户端,都会将自己注册到 Selector 中,如果哪个 Channel 有请求连接事件( ServerSocketChannel)或者是读写事件(SocketChannel),那么这个 Channel
就会处于就绪状态;接着会被 Selector 轮询出来,进行后续的 I/O 操作。这就不会出现 IO 编程中的阻塞状态,所以 NIO 是同步非阻塞的。

四、总结

通过上面的讲解分析,可能还是会有很多同学不能真正理解同步异步、阻塞非阻塞这些概念,毕竟这些是我自己个人的理解和解读,所以我还是非常推荐同学们自己去看看《Netty 权威指南》这本书,和看看 Java 中关于 IO 和 NIO 编程的相关源码,一定要让自己理解地更加深刻。

通过上面的 NIO 源码展示,我相信很多同学会发现使用 Java NIO 来进行开发,会比较的费劲:

  1. Java NIO 的类库和 API 比较复杂,我们需要熟练掌握相关类和接口的使用。
  2. Java NIO 的可靠性是比较低的,例如断开重连、半包问题和序列化都是需要开发者自己去搞定的。
  3. Java NIO 中有一个非常出名的 BUG,那就是关于 epoll 的 bug,它会导致 Selector 空轮询,最终导致 CPU 100%。

所以,如果我们进行 NIO 编程,都会首选 Netty 这款 NIO 框架。而至于 Netty 是如何的强大,那么就需要大家去自己体验和摸索了~

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

更多:Java进阶核心知识集

包含:JVM,JAVA集合,网络,JAVA多线程并发,JAVA基础,Spring原理,微服务,Zookeeper,Kafka,RabbitMQ,Hbase,MongoDB,Cassandra,设计模式,负载均衡,数据库,一致性哈希,JAVA算法,数据结构,加密算法,分布式缓存等等

image

高效学习视频

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
式缓存等等

[外链图片转存中…(img-xZwxFpv6-1713635718342)]

高效学习视频

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值