I/O模型
java支持3种网络编程模型I/O模式:BIO(同步并阻塞)、NIO(同步非阻塞)、AIO(异步非阻塞)
阻塞指的是访问IO的线程是否会阻塞(或等待)。线程访问资源,该资源是否准备就绪的一种处理方式。
阻塞与非阻塞:
同步与异步:
1.1 BIO
BIO:同步阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,可以通过线程池机制改善。
代码示例:
public class ServerDemo {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket=new ServerSocket(9999);
ExecutorService executorService = Executors.newCachedThreadPool();
while (true){
Socket socket=serverSocket.accept();
System.out.println("有客户端连接");
executorService.execute(new Runnable() {
@Override
public void run() {
InputStream inputStream = null;
try {
inputStream = socket.getInputStream();
byte[] b=new byte[1024];
int read = inputStream.read(b);
System.out.println("收到客户端信息:"+new String(b,0,read));
OutputStream outputStream = socket.getOutputStream();
outputStream.write("你好客户端".getBytes(Charset.defaultCharset()));
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
}
}
public class ClientDemo {
public static void main(String[] args) throws IOException {
Socket socket=new Socket("127.0.0.1",9999);
OutputStream outputStream = socket.getOutputStream();
outputStream.write("你好服务端".getBytes(Charset.defaultCharset()));
InputStream inputStream = socket.getInputStream();
byte[] bytes=new byte[1024];
inputStream.read(bytes);
System.out.println("收到服务端消息:"+new String(bytes));
socket.close();
}
}
BIO问题分析:
- 每个请求都需要创建独立的线程,与对应的客户端进行数据Read、业务处理、数据Write
- 并发数较大时,需要创建大量线程来处理连接,系统资源占用较大
- 连接建立后,如果当前线程暂时没有数据可读,则线程就阻塞在Read操作上,造成资源阻塞。
1.2 NIO
NIO:同步非阻塞,服务器实现模式为一个线程处理多个请求(连接),即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求就进行处理。
代码示例:
public class NioSelectorServer {
public static void main(String[] args) throws IOException, InterruptedException {
//1.打开服务器通道
ServerSocketChannel serverSocketChannel=ServerSocketChannel.open();
//2.绑定端口号
serverSocketChannel.bind(new InetSocketAddress(9999));
//3.设置通道为非阻塞
serverSocketChannel.configureBlocking(false);
//4.创建选择器
Selector selector = Selector.open();
//5.将服务端通道注册到选择器上,并指定注册监听事件为OP_ACCEPT
serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT);
while (true){
//6.检查选择器释放有事件
// int select = selector.select(2000);
int select = selector.select();
if(select==0){
System.out.println("无连接");
continue;
}
//7.获取事件集合
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()){
SelectionKey selectionKey = iterator.next();
//8.判断事件是否是连接事件
if(selectionKey.isAcceptable()){
//9.得到客户端通道,并将通道注册到选择器上
SocketChannel socketChannel = serverSocketChannel.accept();
System.out.println("有客户端连接");
socketChannel.configureBlocking(false);
socketChannel.register(selector,SelectionKey.OP_READ);
}
//10.判断是否是读就绪事件
else if(selectionKey.isReadable()){
SocketChannel channel = (SocketChannel)selectionKey.channel();
ByteBuffer byteBuffer=ByteBuffer.allocate(1024);
//11.得到客户端通道,读取数据到缓冲区
int read = 0;
read = channel.read(byteBuffer);
System.out.println("客户端消息:"+new String(byteBuffer.array()));
System.out.println("模拟业务处理中。。。");
Thread.sleep(5000);
if(read>0){
//12.回写数据给客户端
channel.write(ByteBuffer.wrap("你好客户端".getBytes(StandardCharsets.UTF_8)));
channel.close();
}
}
//13.从集合删除对应事件,防止二次处理
iterator.remove();
}
}
}
}
1.3 AIO
AIO引入了异步通道的概念,采用了Proactor模式,简化了程序编写。它的特点是先由操作系统完成后才通知服务端程序启动线程去处理,一般适用于连接数较多且连接时间比较长的应用。
Proactor模式是一个消息异步通知的设计模式,Proactor通知的部署就绪事件,而是操作完成事件。
适用场景:
- BIO适用于连接数比较小且固定的架构,这种方式对服务器资源要求比较高
- NIO适用于连接数目多且比较短的架构,比如聊天服务器,弹幕系统,服务期间通讯等
- AIO适用于连接数多且连接比较长的架构,比如相册服务器。
NIO编程
2.1 NIO介绍
Java NIO是JDK提供的新API。NIO有三个核心部分:Channel,Buffer,Selector。NIO是面向缓冲区编程的。JAVA NIO的非阻塞模式,是一个线程从某个通道发送或读取数据,仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取,而不是阻塞线程。
通俗理解:NIO是可以做到用一个线程来处理多个操作的。假设有1000个请求过来,根据实际情况,可以分配50或100个线程来处理。不像BIO那样,非得分配1000个线程。
NIO和BIO比较
- BIO是以流的方式处理数据,而NIO是以缓冲区的方式处理数据
- BIO是阻塞的,NIO是非阻塞的
- BIO基于字节流和字符流操作,而NIO是基于Channel和Buffer进行操作。NIO基于Selector监听多个通道的事件。