java非阻塞NIO和阻塞IO
1 非阻塞NIO和阻塞IO
1.1 定义
阻塞IO:线程被阻塞,去处理一个读取和写入,中间如果有等待时间,则线程被占用,也不能处理其他任务;
非阻塞IO(new IO):引入了通道的概念,一个连接对应一个通道,为每个通道配置缓冲区,线程去轮询查看的通道的状态,如果某个通道上有数据准备好了,则通道状态会发生改变,线程就去处理这个读写操作;
1.2 非阻塞实现的核心Selector
Selector 一般称为选择器 ,或者多路复用器。在通道上建立socket连接,然后调用channel.register(selector, Selectionkey.OP_READ);将通道注册到Selector上,并且申明这个连接是读(READ)、写(WRITE)、接受(ACCEPT)、连接( CONNECT),或者四个功能都包括;这样就可以用一个线程,一个Selector去管理多个连接;selector会去循环查询各个通道的状态,如果通道状态发生改变,会去判断通道的类型(读、写、接受、连接),然后再对应去做处理;
(1)Selector的创建
通过调用Selector.open()方法创建一个Selector对象,如下:
Selector selector = Selector.open();
(2)注册Channel到Selector
channel.configureBlocking(false);
SelectionKey key = channel.register(selector, Selectionkey.OP_READ);
第二个参数是通道的类型,有四个值,方便位运算;
读 : SelectionKey.OP_READ ( 1)
写 : SelectionKey.OP_WRITE ( 4)
连接 : SelectionKey.OP_CONNECT ( 8)
接收 : SelectionKey.OP_ACCEPT ( 16)
若注册时不止监听一个事件,则可以使用“位或”操作符连接。
1.3 SelectableChannle通道上建立连接
(1)非阻塞通道
AbstractSelectableChannel抽象类继承了SelectableChannle接口, SocketChannel、ServerSocketChannel、DatagramChannel都是直接继承了 AbstractSelectableChannel抽象类 。SocketChannel是用于socket客户端,ServerSocketChannel是用于socket服务器;
(2)阻塞通道
FileChannel还是不能实现非阻塞,对文件的读写IO,不能同时写入一个文件。
1.4 SelectionKey通道状态
SelectionKey 代表各个通道的状态信号,通过调用选择器的函数来获取到所有有信号的通道的状态,Set<SelectionKey> keys = selector.selectedKeys();然后在通过SelectionKey的方法去判断是哪种信号。
(1)key.attachment(); //返回SelectionKey的attachment,attachment可以在注册channel的时候指定。
(2)key.channel(); // 返回该SelectionKey对应的channel。
(3)key.selector(); // 返回该SelectionKey对应的Selector。
(4)key.interestOps(); //返回代表需要Selector监控的IO操作的bit mask
我们可以通过与操作来判断Selector是否对Channel的某种事件感兴趣,感兴趣的boolean为1;
int interestSet = selectionKey.interestOps();
boolean isInterestedInAccept = (interestSet & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT;
boolean isInterestedInConnect = interestSet & SelectionKey.OP_CONNECT;
boolean isInterestedInRead = interestSet & SelectionKey.OP_READ;
boolean isInterestedInWrite = interestSet & SelectionKey.OP_WRITE;
(5)key.readyOps(); // 返回一个bit mask,代表在相应channel上可以进行的IO操作。可以通过与运算去判断是否准备好;
(6)直接调用SelectionKey对象的函数判断信号状态
boolean isReadable() 检测 Channal 中读事件是否就绪
boolean isWritable() 检测 Channal 中写事件是否就绪
boolean isConnectable() 检测 Channel 中连接是否就绪
boolean isAcceptable() 检测 Channel 中接收是否就绪
1.5 阻塞IO使用实例
客户端向服务器端发送一张图片
package com.happybks.nio.nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
java.nio.
.Paths;
java.nio.
.StandardOpenOption;
import org.junit.Test;
{
{
SocketChannel socketChannel = SocketChannel.open(
InetSocketAddress(
,
));
ByteBuffer buf=ByteBuffer.allocate(
);
FileChannel inFileChannel=FileChannel.open(Paths.get(
),
StandardOpenOption.
);
(inFileChannel.
(buf)!=-
){
buf.flip();
socketChannel.
(buf);
buf.clear();
}
socketChannel.shutdownOutput();
len =
;
((len = socketChannel.
(buf)) !=-
){
buf.flip();
System.out.
(
String(buf.array(),
,len));
}
inFileChannel.close();
socketChannel.close();
}
{
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.bind(
InetSocketAddress(
));
SocketChannel socketChannel = serverSocketChannel.accept();
FileChannel outFileChannel = FileChannel.open(Paths.get(
),
StandardOpenOption.
,StandardOpenOption.CREATE);
ByteBuffer buf=ByteBuffer.allocate(
);
(socketChannel.
(buf)!=-
){
buf.flip();
outFileChannel.
(buf);
buf.clear();
}
buf.put(
.getBytes());
buf.flip();
socketChannel.
(buf);
socketChannel.close();
outFileChannel.close();
serverSocketChannel.close();
}
}
1.6 非阻塞IO使用实例
package com.happybks.nio.nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Date;
import java.util.Iterator;
import org.junit.Test;
public class TestNonBlockingNIO {
//客户端
@Test
public void client() throws IOException{
//1、获取通道
SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1",8888));
//2、切换非阻塞模式
socketChannel.configureBlocking(false);
//3、分配指定大小的缓冲区
ByteBuffer buf=ByteBuffer.allocate(1024);
//4、发送数据服务器
buf.put(new Date().toString().getBytes());
buf.flip();
socketChannel.write(buf);
buf.clear();
//5、关闭通道
socketChannel.close();
}
//服务端
@Test
public void server() throws IOException{
//1、获取通道
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
//2、切换非阻塞模式
serverSocketChannel.configureBlocking(false);
//3、绑定连接
serverSocketChannel.bind(new InetSocketAddress(8888));
//4、获取选择器
Selector selector = Selector.open();
//5、将通道注册到选择器上(第二个选项参数叫做选择键,用于告诉选择器需要监控这个通道的什么状态或者说什么事件(读、写、连接、接受))
//选择键是整型值,如果需要监控该通道的多个状态或事件,可以将多个选择键用位运算符“或”“|”来连接
//这里服务端首先要监听客户端的接受状态
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
//6、轮询式地获取选择器上已经“准备就绪”的事件
while(selector.select() > 0){
//7、获取当前选择中所有注册的“选择键(已就绪的监听事件)”
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while(iterator.hasNext()){
//8、获取准备“就绪”的是事件
SelectionKey sk=iterator.next();
//9、判断是什么事件准备就绪
if(sk.isAcceptable()){
//10、若接受就绪,获取客户端连接
SocketChannel socketChannel = serverSocketChannel.accept();
//11、客户端连接socketChannel也需要切换非阻塞模式
socketChannel.configureBlocking(false);
//12、将该通道注册到选择器上,监控客户端socketChannel的读就绪事件
socketChannel.register(selector, SelectionKey.OP_READ);
}
else if(sk.isReadable()){
//13、获取当前选择器上“读就绪”状态的通道
SocketChannel socketChannel = (SocketChannel) sk.channel();
//14、读取数据
ByteBuffer buf=ByteBuffer.allocate(1024);
int len=0;
while((len=socketChannel.read(buf))>0){
buf.flip();
System.out.println(new String(buf.array(),0,len));
buf.clear();
}
}
//15、取消选择键SelectionKey
iterator.remove();
}
}
}
}
自己开发了一个股票智能分析软件,功能很强大,需要的点击下面的链接获取:
https://www.cnblogs.com/bclshuai/p/11380657.html
百度云盘下载地址:
链接:https://pan.baidu.com/s/1swkQzCIKI3g3ObcebgpIDg
提取码:mc8l
微信公众号获取最新的软件和视频介绍
QStockView
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix