公司建设网站时的注意事项,网站建设实验总结报告,西湖专业网站设计公司,邯郸市建设局网站政策文章目录 BIO优缺点示例代码 NIO优缺点示例代码 AIO优缺点示例代码 总结 前些天发现了一个巨牛的人工智能学习网站#xff0c;通俗易懂#xff0c;风趣幽默#xff0c;忍不住分享一下给大家。点击跳转到网站。 BIO、NIO和AIO是Java编程语言中用于处理输入输出#xff08;IO… 文章目录 BIO优缺点示例代码 NIO优缺点示例代码 AIO优缺点示例代码 总结 前些天发现了一个巨牛的人工智能学习网站通俗易懂风趣幽默忍不住分享一下给大家。点击跳转到网站。 BIO、NIO和AIO是Java编程语言中用于处理输入输出IO操作的三种不同的机制它们分别代表同步阻塞I/O同步非阻塞I/O和异步非阻塞I/O。
BIO
BIOBlocking IO 是最传统的IO模型也称为同步阻塞IO。它实现的是同步阻塞模型即服务器实现模式为一个连接一个线程即客户端有连接请求时服务器端就需要启动一个线程进行处理。如果这个连接不做任何事情会造成不必要的线程开销并且线程在进行IO操作期间是被阻塞的无法进行其他任务。在高并发环境下BIO的性能较差因为它需要为每个连接创建一个线程而且线程切换开销较大不过可以通过线程池机制改善。BIO适合一些简单的、低频的、短连接的通信场景例如HTTP请求。 优缺点
优点
简单易用 BIO模型的编程方式相对简单易于理解和使用。可靠性高 由于阻塞特性IO操作的结果是可靠的。
缺点
阻塞等待 当一个IO操作被阻塞时线程会一直等待无法执行其他任务导致资源浪费。并发能力有限 每个连接都需要一个独立的线程当连接数增加时线程数量也会增加造成资源消耗和性能下降。由于I/O操作是同步的客户端的连接需要等待服务器响应会降低系统的整体性能。
示例代码
服务端代码
import java.io.*;
import java.net.*;public class BIOServer {public static void main(String[] args) throws IOException {ServerSocket serverSocket null;Socket clientSocket null;try {//创建服务端serverSocket new ServerSocket(8888);System.out.println(服务端已启动等待客户端连接...);while (true){// 监听客户端请求接收不到请求会一直等待clientSocket serverSocket.accept();int port clientSocket.getPort();InetAddress inetAddress clientSocket.getInetAddress();System.out.println(客户端 inetAddress:port 连接成功);//处理客户端消息new Thread(new ServerThread(clientSocket)).start();}} catch (IOException e) {System.out.println(客户端连接失败 e.getMessage());} finally {try {if (clientSocket ! null) {clientSocket.close();}if (serverSocket ! null) {serverSocket.close();}} catch (IOException e) {System.out.println(关闭资源失败 e.getMessage());}}}
}/*** 服务端线程处理类*/
class ServerThread implements Runnable{private Socket clientSocket;public ServerThread(Socket clientSocket) {this.clientSocket clientSocket;}Overridepublic void run() {//获取客户端输入流以便接收客户端数据try {BufferedReader in new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));//获取客户端输出流以便向客户端发送数据PrintWriter out new PrintWriter(clientSocket.getOutputStream());int port clientSocket.getPort();InetAddress inetAddress clientSocket.getInetAddress();String address inetAddress:port;String inputLine;while ((inputLine in.readLine()) ! null) {//接收客户端消息System.out.println(客户端address发来消息 inputLine);//给客户端发送消息out.println(服务端已接收到消息并回复inputLine);out.flush();}} catch (IOException e) {e.printStackTrace();}}
}客户端代码
public class BIOClient {public static void main(String[] args) throws IOException {Socket clientSocket null;BufferedReader in null;PrintWriter out null;try {//绑定服务端ip和端口号clientSocket new Socket(localhost, 8888);System.out.println(连接服务端成功);//获取输入流接收服务端消息in new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));//获取输出流,给服务端发送消息out new PrintWriter(clientSocket.getOutputStream(), true);Scanner scanner new Scanner(System.in);while (true){System.out.print(给服务端发送消息);String msg scanner.nextLine();out.println(msg);String response;if ((response in.readLine()) ! null) {//接收服务端响应System.out.println(服务端响应 response);}}} catch (IOException e) {System.out.println(连接服务端失败 e.getMessage());} finally {try {if (in ! null) {in.close();}if (out ! null) {out.close();}if (clientSocket ! null) {clientSocket.close();}} catch (IOException e) {System.out.println(关闭资源失败 e.getMessage());}}}
}运行结果 上述代码只是简单演示了如何使用BIO模型一个服务端接收并处理多个客户端请求情况。在这里创建了3个类分别为服务端 BIOServer、多线程客户端处理类ServerThread和 客户端BIOClient接着分别启动服务端BIOServer和两个客户端BIOClient并在客户端中接收键盘输入的字符串发送给服务端最后服务端把接收到的数据再返回给客户端。
由于BIO的特性所以在服务端中需要为每个连接创建一个线程也可以通过线程池进行优化这里只是简单演示不做过多设计。
NIO
NIO是Java 1.4引入的新IO模型也称为同步非阻塞IO它提供了一种基于事件驱动的方式来处理I/O操作。
相比于传统的BIO模型NIO采用了Channel、Buffer和Selector等组件线程可以对某个IO事件进行监听并继续执行其他任务不需要阻塞等待。当IO事件就绪时线程会得到通知然后可以进行相应的操作实现了非阻塞式的高伸缩性网络通信。在NIO模型中数据总是从Channel读入Buffer或者从Buffer写入Channel这种模式提高了IO效率并且可以充分利用系统资源。
NIO主要有三部分组成选择器Selector、缓冲区Buffer和通道Channel。Channel是一个可以进行数据读写的对象所有的数据都通过Buffer来处理这种方式避免了直接将字节写入通道中而是将数据写入包含一个或者多个字节的缓冲区。在多线程模式下一个线程可以处理多个请求这是通过将客户端的连接请求注册到多路复用器上然后由多路复用器轮询到连接有I/O请求时进行处理。
对于NIO如果从特性来看它是非阻塞式ION是Non-Blocking的意思如果从技术角度NIO对于BIO来说是一个新技术N的意思是New的意思。所以NIO也常常被称作Non-Blocking I/O或New I/O。
NIO适用于连接数目多且连接比较短轻操作的架构例如聊天服务器、弹幕系统、服务器间通讯等。它通过引入非阻塞通道的概念提高了系统的伸缩性和并发性能。同时NIO的使用也简化了程序编写提高了开发效率。 优缺点
优点
高并发性 使用选择器Selector和通道Channel的NIO模型可以在单个线程上处理多个连接提供更高的并发性能。节省资源 相对于BIONIO需要更少的线程来处理相同数量的连接节省了系统资源。灵活性 NIO提供了多种类型的Channel和Buffer可以根据需要选择适合的类型。NIO允许开发人员自定义协议、编解码器等组件从而提高系统的灵活性和可扩展性。高性能 NIO采用了基于通道和缓冲区的方式来读写数据这种方式比传统的流模式更高效。可以减少数据拷贝次数提高数据处理效率。内存管理NIO允许用户手动管理缓冲区的内存分配和回收避免了传统I/O模型中的内存泄漏问题。
缺点
编程复杂 相对于BIONIO的编程方式更加复杂需要理解选择器和缓冲区等概念也需要考虑多线程处理和同步问题。可靠性较低 NIO模型中一个连接的读写操作是非阻塞的无法保证IO操作的结果是可靠的可能会出现部分读写或者错误的数据。
NIO适合一些复杂的、高频的、长连接的通信场景例如聊天室、网络游戏等。
示例代码
在看代码之前先了解NIO中3个非常重要的组件选择器Selector、缓冲区Buffer 和 通道Channel 通道ChannelChannel是NIO中用于数据读写的双向通道可以从通道中读取数据也可以将数据写入通道。与传统的IO不同Channel是双向的可以同时进行读写操作而传统的IO只能通过InputStream或OutputStream进行单向读写。Java NIO中常见的Channel有FileChannel文件读写、DatagramChannelUDP协议、SocketChannelTCP协议和ServerSocketChannel监听TCP连接请求等。 缓冲区Buffer Buffer是NIO中用于存储数据的缓冲区可以理解为一个容器可以从中读取数据也可以将数据写入其中。Buffer具有一组指针来跟踪当前位置、限制和容量等属性。Java NIO中提供了多种类型的Buffer例如ByteBuffer、CharBuffer、ShortBuffer、IntBuffer等。每种类型的Buffer都有自己特定的读写方法可以使用get()和put()等方法来读写缓冲区中的数据。 选择器Selector Selector是NIO中用于监控多个Channel的选择器可以实现单线程管理多个Channel。Selector可以检测多个Channel是否有事件发生包括连接、接收、读取和写入等事件并根据不同的事件类型进行相应处理。Selector可以有效地减少单线程管理多个Channel时的资源占用提高程序的运行效率。
NIO的操作流程如下
打开通道并设置为非阻塞模式。将通道注册到选择器上并指定感兴趣的事件类型如连接打开、可读等。线程通过调用选择器的select()方法等待事件发生。当有一个或多个事件发生时线程可以从选择器中获取已经准备好的通道并进行相应的IO操作。IO操作完成后关闭通道和选择器。
下面通过两段代码展示一下NIO的操作流程和使用方式。
服务端代码
public class NIOServer {public static void main(String[] args) throws IOException {Selector selector Selector.open();// 创建一个ServerSocketChannel并绑定到指定的端口ServerSocketChannel serverSocketChannel ServerSocketChannel.open();serverSocketChannel.bind(new InetSocketAddress(9999));// 设置为非阻塞模式serverSocketChannel.configureBlocking(false);// 将ServerSocketChannel注册到Selector上并监听OP_ACCEPT事件serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);System.out.println(服务器已启动等待客户端连接...);while (true) {// 阻塞等待事件发生selector.select();SetSelectionKey selectedKeys selector.selectedKeys();IteratorSelectionKey keyIterator selectedKeys.iterator();while (keyIterator.hasNext()) {SelectionKey key keyIterator.next();if (key.isAcceptable()) { // 处理连接请求事件SocketChannel client serverSocketChannel.accept();client.configureBlocking(false);//监听OP_ACCEPT事件client.register(selector, SelectionKey.OP_READ);} else if (key.isReadable()) {SocketChannel client (SocketChannel) key.channel();client.getRemoteAddress();//分配缓存区容量ByteBuffer buffer ByteBuffer.allocate(1024);client.read(buffer);String output new String(buffer.array()).trim();Socket socket client.socket();InetAddress inetAddress socket.getInetAddress();int port socket.getPort();String clientInfo inetAddress:port;String message String.format(来自客户端 %s , 消息%s, clientInfo , output);System.out.println(message);System.out.print(回复消息: );writeMessage(selector, client, buffer);}keyIterator.remove();}}}private static void writeMessage(Selector selector, SocketChannel client, ByteBuffer buffer) throws IOException {Scanner scanner new Scanner(System.in);String message scanner.nextLine();buffer.clear();buffer.put(message.getBytes());//从写模式切换到读模式buffer.flip();while (buffer.hasRemaining()) {client.write(buffer);}// 重新监听OP_ACCEPT事件client.register(selector, SelectionKey.OP_READ);}
}客户端代码
/*** author 公众号索码理(suncodernote)*/
public class NIOClient {public static void main(String[] args) throws IOException {Selector selector Selector.open();SocketChannel socketChannel SocketChannel.open();socketChannel.configureBlocking(false);socketChannel.connect(new InetSocketAddress(localhost, 9999));socketChannel.register(selector, SelectionKey.OP_CONNECT);while (true) {selector.select();SetSelectionKey selectedKeys selector.selectedKeys();IteratorSelectionKey keyIterator selectedKeys.iterator();while (keyIterator.hasNext()) {SelectionKey key keyIterator.next();if (key.isConnectable()) {SocketChannel client (SocketChannel) key.channel();if (client.isConnectionPending()) {client.finishConnect();}System.out.print(Enter message to server: );Scanner scanner new Scanner(System.in);String message scanner.nextLine();ByteBuffer buffer ByteBuffer.wrap(message.getBytes());client.write(buffer);client.register(selector, SelectionKey.OP_READ);} else if (key.isReadable()) {SocketChannel client (SocketChannel) key.channel();ByteBuffer buffer ByteBuffer.allocate(1024);client.read(buffer);String output new String(buffer.array()).trim();System.out.println(来自客户端的消息: output);System.out.print(输入消息: );// 和服务端代码一样writeMessage(selector, client, buffer);}keyIterator.remove();}}}
}运行结果 上面代码新建了两个类服务端NIOServer和客户端NIOClient 通过上面代码和运行结果可以发现在服务端和客户端进行通信时我们并没有新建线程类进行通信这也是NIO和BIO最大的区别之一。
需要注意的是虽然NIO提高了系统的并发性能和伸缩性但也带来了更高的编程复杂度和更难的调试问题。因此在使用Java NIO时需要仔细考虑其适用场景和编程模型。
AIO
Java AIOAsynchronous I/O是Java提供的异步非阻塞IO编程模型从Java 7版本开始支持AIO又称NIO 2.0。
相比于NIO模型AIO模型更进一步地实现了异步非阻塞IO提高了系统的并发性能和伸缩性。在NIO模型中虽然可以通过多路复用器处理多个连接请求但仍需要在每个连接上进行读写操作这仍然存在一定的阻塞。而在AIO模型中所有的IO操作都是异步的不会阻塞任何线程可以更好地利用系统资源。
AIO模型有以下特性
异步能力AIO模型的最大特性是异步能力对于socket和I/O操作都有效。读写操作都是异步的完成后会自动调用回调函数。回调函数在AIO模型中当一个异步操作完成后会通知相关线程进行后续处理这种处理方式称为“回调”。回调函数可以由开发者自行定义用于处理异步操作的结果。非阻塞AIO模型实现了完全的异步非阻塞IO不会阻塞任何线程可以更好地利用系统资源。高性能由于AIO模型的异步能力和非阻塞特性它可以更好地处理高并发、高伸缩性的网络通信场景进一步提高系统的性能和效率。操作系统支持AIO模型需要操作系统的支持因此在不同的操作系统上可能会有不同的表现。在Linux内核2.6版本之后增加了对真正异步IO的实现。
优缺点
优点 非阻塞AIO的主要优点是它是非阻塞的。这意味着在读写操作进行时程序可以继续执行其他任务。这对于需要处理大量并发连接的高性能服务器来说是非常有用的。 高效由于AIO可以处理大量并发连接因此它通常比同步I/O例如Java的传统I/O和NIO更高效。 简化编程模型AIO使用了回调函数这使得编程模型相对简单。当一个操作完成时会自动调用回调函数无需程序员手动检查和等待操作的完成。
缺点 复杂性虽然AIO的编程模型相对简单但是由于其非阻塞的特性编程复杂性可能会增加。例如需要处理操作完成的通知以及可能的并发问题。 资源消耗AIO可能会消耗更多的系统资源。因为每个操作都需要创建一个回调函数如果并发连接数非常大可能会消耗大量的系统资源。 可移植性AIO在某些平台上可能不可用或者性能不佳。因此如果需要跨平台的可移植性可能需要考虑使用其他I/O模型。
AIO适合一些极端的、超高频的、超长连接的通信场景例如云计算、大数据等。
需要注意的是目前AIO模型还没有广泛应用Netty等网络框架仍然是基于NIO模型。
示例代码
服务端
/*** author 公众号索码理(suncodernote)*/
public class AIOServer {public static void main(String[] args) throws Exception {// 创建一个新的异步服务器套接字通道绑定到指定的端口上final AsynchronousServerSocketChannel serverChannel AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(5000));System.out.println(服务端启动成等待客户端连接。);// 开始接受新的客户端连接serverChannel.accept(null, new CompletionHandlerAsynchronousSocketChannel, Void() {Overridepublic void completed(AsynchronousSocketChannel clientChannel, Void att) {// 当一个新的连接完成时再次接受新的客户端连接serverChannel.accept(null, this);// 创建一个新的缓冲区来读取数据ByteBuffer buffer ByteBuffer.allocate(1024);try {InetSocketAddress clientAddress (InetSocketAddress) clientChannel.getRemoteAddress();InetAddress clientIP clientAddress.getAddress();int clientPort clientAddress.getPort();System.out.println(客户端 clientIP : clientPort 连接成功。);} catch (IOException e) {e.printStackTrace();}// 从异步套接字通道中读取数据clientChannel.read(buffer, buffer, new ReadCompletionHandler(clientChannel));}Overridepublic void failed(Throwable exc, Void attachment) {System.out.println(Failed to accept a connection);}});// 保持服务器开启Thread.sleep(Integer.MAX_VALUE);}
}读处理程序
/*** author 公众号索码理(suncodernote)*/
public class ReadCompletionHandler implements CompletionHandlerInteger, ByteBuffer {private AsynchronousSocketChannel channel;public ReadCompletionHandler(AsynchronousSocketChannel channel) {this.channel channel;}Overridepublic void completed(Integer result, ByteBuffer attachment) {// 当读取完成时反转缓冲区并打印出来attachment.flip();byte[] bytes new byte[attachment.remaining()];attachment.get(bytes);System.out.println(收到的消息: new String(bytes , StandardCharsets.UTF_8));attachment.clear();// 从键盘读取输入Scanner scanner new Scanner(System.in);System.out.print(输入消息: );String message scanner.nextLine();System.out.println();// 写入数据到异步套接字通道channel.write(ByteBuffer.wrap(message.getBytes()));channel.read(attachment , attachment , new ReadCompletionHandler(channel));}Overridepublic void failed(Throwable exc, ByteBuffer attachment) {System.out.println(Failed to read message);}
}客户端
/*** author 公众号索码理(suncodernote)*/
public class AIOClient {public static void main(String[] args) throws Exception {// 创建一个新的异步套接字通道AsynchronousSocketChannel clientChannel AsynchronousSocketChannel.open();// 连接到服务器clientChannel.connect(new InetSocketAddress(localhost, 5000), null, new CompletionHandlerVoid, Void() {Overridepublic void completed(Void result, Void attachment) {System.out.println(连接到服务端成功。);}Overridepublic void failed(Throwable exc, Void attachment) {System.out.println(Failed to connect server);}});// 从键盘读取输入Scanner scanner new Scanner(System.in);System.out.print(发送消息: );String message scanner.nextLine();// 写入数据到异步套接字通道clientChannel.write(ByteBuffer.wrap(message.getBytes()), null, new CompletionHandlerInteger, Void() {Overridepublic void completed(Integer result, Void attachment) {ByteBuffer buffer ByteBuffer.allocate(1024);clientChannel.read(buffer, buffer, new ReadCompletionHandler(clientChannel));}Overridepublic void failed(Throwable exc, Void attachment) {System.out.println(Failed to write message);}});// 保持客户端开启Thread.sleep(Integer.MAX_VALUE);}
}测试结果
服务端界面 客户端1 客户端2 客户端3 上述示例代码中通过一个服务端(AIOServer)和3个客户端(AIOClient)的通信简单演示了AIO的使用。可以发现AIO和NIO的使用方式基本一致数据都是从Channel读入Buffer或者从Buffer写入Channel中不同的是AIO是实现了异步非阻塞。
总结
Java中的BIO、NIO和AIO都是处理输入/输出I/O操作的模型但它们在处理方式和效率上有所不同。 BIOBlocking I/OBIO是最传统的I/O模型它的操作都是阻塞的。这意味着当一个线程发起一个I/O操作后必须等待操作完成才能进行其他任务。因此BIO在处理大量并发连接时效率较低但其编程模型简单。 NIONon-blocking I/ONIO是非阻塞的I/O模型它允许线程在等待I/O操作完成时进行其他任务。NIO引入了Channel和Buffer的概念以及Selector用于多路复用。NIO适合处理大量并发连接但其编程模型相对复杂。 AIOAsynchronous I/OAIO是真正的异步I/O模型应用程序无需等待I/O操作的完成当操作完成时操作系统会通知应用程序。AIO使用回调函数或Future对象来获取操作结果适合处理大量并发连接其编程模型相对简单。
总之BIO、NIO和AIO各有优缺点适用的场景也不同。BIO适合连接数目较少且固定的架构NIO适合连接数目多但是并发读写操作相对较少的场景AIO则适合连接数目多且并发读写操作也多的场景。在选择使用哪种I/O模型时需要根据具体的应用场景和需求进行权衡。