怎么做兼职类网站,单位网站建设ppt,广州网站制作哪里好,ag娱乐建设网站在Java中#xff0c;处理I/O#xff08;输入/输出#xff09;操作的方式经历了从BIO#xff08;Blocking I/O#xff0c;阻塞式I/O#xff09;到NIO#xff08;New I/O 或 Non-blocking I/O#xff0c;新I/O或非阻塞式I/O#xff09;的演变。这两种模型在设计和使用上… 在Java中处理I/O输入/输出操作的方式经历了从BIOBlocking I/O阻塞式I/O到NIONew I/O 或 Non-blocking I/O新I/O或非阻塞式I/O的演变。这两种模型在设计和使用上有显著的区别它们分别适用于不同的应用场景。本文将深入探讨这两种I/O模型的原理、区别以及各自的使用场景。 一、Java BIOBlocking I/O 
Java BIO是一种同步阻塞的I/O模型它是Java最早提供的I/O模型。在进行读写操作的时候若使用BIO进行通信则操作不再受到操作系统的控制而是由应用程序自己控制。在BIO中数据的读取写入必须阻塞在一个线程内等待其完成。 BIO的工作原理相对简单当一个连接建立后服务端会为每个连接创建一个新的线程进行处理直到连接关闭。这种方式在并发连接数较少时表现良好但当并发连接数增加时由于每个连接都需要一个独立的线程系统的资源消耗会急剧增加导致性能下降。  BIO的另一个特点是它是面向流的即一次只能处理一个输入或输出请求且这些请求是单向的。这种处理方式在某些场景下可能不够灵活。   
二、Java NIONew I/O 或 Non-blocking I/O 
Java NIO是一种同步非阻塞的I/O模型它引入了多路复用器和缓冲区的概念使得一个线程可以处理多个连接提高了系统的吞吐量和性能。 
NIO的核心组件包括Channel通道、Buffer缓冲区和Selector选择器。Channel类似于BIO中的流但它可以进行双向操作并且支持异步读写。Buffer是NIO中的数据容器它用于在Channel中进行数据的读写操作。Selector则是NIO中的多路复用器它可以同时监听多个Channel的状态当Channel有数据可读或可写时Selector会通知相应的线程进行处理。 
JAVA中Buffer和Channel实现  NIO的工作原理是基于Reactor模式反应堆模式的。当一个连接建立后服务端会将该连接注册到Selector上并指定感兴趣的事件如读事件、写事件等。然后服务端会启动一个或多个线程不断轮询Selector检查是否有感兴趣的事件发生。如果有事件发生线程会处理相应的事件然后继续轮询。这种方式使得一个线程可以处理多个连接降低了系统的资源消耗。  
三、BIO与NIO的区别 同步与异步BIO是同步的读写操作必须等待数据准备好后才能进行而NIO是同步非阻塞的读写操作不再受到数据准备状态的限制可以进行读写操作但可能需要等待数据真正写入或读取完成。  阻塞与非阻塞BIO是阻塞的在进行读写操作的时候若使用BIO进行通信则操作必须阻塞在一个线程内等待其完成而NIO是非阻塞的在进行读写操作的时候若使用NIO进行通信则操作不再受到阻塞的限制可以进行其他操作。  面向流与面向缓冲BIO是面向流的一次只能处理一个输入或输出请求而NIO是面向缓冲区的一次可以处理多个输入或输出请求。  选择器SelectorNIO有选择器而BIO没有。选择器能够检测多个注册的通道上是否有事件发生如果有事件发生便获取事件然后针对每个事件进行相应的响应处理这样就可以只用一个单线程去管理多个通道也就是管理多个连接。  
四、为什么说BIO是同步阻塞,而NIO是同步非阻塞 
1、BIOBlocking I/O为什么是同步阻塞的 
同步在BIO模型中当应用程序发起一个I/O请求例如读取或写入数据时它必须等待操作系统内核完成这个请求。在此期间应用程序的线程是阻塞的不能做其他事情直到I/O操作完成。这种等待内核响应的行为被称为同步。阻塞由于应用程序的线程在等待I/O操作完成期间不能做其他工作因此我们说这个线程是被阻塞的。阻塞意味着线程不能继续执行后续的代码或处理其他任务直到当前的I/O操作完成。 
2、NIONew I/O 或 Non-blocking I/O为什么是同步非阻塞的 
同步在NIO模型中虽然引入了新的机制和概念如选择器、通道和缓冲区但I/O操作仍然是同步的。这意味着当应用程序发起一个I/O请求时它仍然需要等待操作系统内核处理这个请求。NIO的同步性体现在它并没有完全消除等待但改变了等待的方式和效率。非阻塞与BIO不同NIO模型通过使用选择器和通道允许单个线程处理多个I/O通道。当一个通道上的I/O操作不再受到阻塞的限制时例如可以立即返回而不是等待操作完成我们说它是非阻塞的。这并不意味着I/O操作本身不需要时间而是说在等待操作完成的过程中线程可以转而处理其他通道的I/O操作或执行其他任务。 
BIO模型因其简单的编程模型和直观的控制流程而易于理解和使用但在处理大量并发连接时可能会因为每个连接都需要一个线程而变得效率低下。 
NIO模型通过引入选择器和通道使得单个线程可以处理多个连接从而提高了系统的吞吐量和可伸缩性。虽然NIO的编程模型相对复杂但它为处理高并发和大数据量的场景提供了更有效的解决方案。 
五、BIO实现文件复制 
使用BIOBlocking I/O阻塞式I/O模型实现文件复制涉及到使用FileInputStream和FileOutputStream类。以下例展示了如何使用BIO复制文件 
import java.io.FileInputStream;  
import java.io.FileOutputStream;  
import java.io.IOException;  public class FileCopyBIO {  public static void main(String[] args) {  String sourceFilePath  path/to/source/file.txt;  String targetFilePath  path/to/target/file.txt;  try (FileInputStream inputStream  new FileInputStream(sourceFilePath);  FileOutputStream outputStream  new FileOutputStream(targetFilePath)) {  byte[] buffer  new byte[1024]; // 缓冲区用于临时存储读取的数据  int bytesRead;  // 读取源文件并写入目标文件  while ((bytesRead  inputStream.read(buffer)) ! -1) {  outputStream.write(buffer, 0, bytesRead);  }  System.out.println(File copied successfully!);  } catch (IOException e) {  e.printStackTrace();  }  }  
}在这个例子中我们创建了一个FileInputStream对象来读取源文件和一个FileOutputStream对象来写入目标文件。我们使用一个字节数组buffer作为缓冲区来临时存储从源文件读取的数据。while循环会持续读取数据直到没有更多数据可读即read方法返回-1。 
每次调用inputStream.read(buffer)时它会阻塞直到有一些数据可以读取或者到达文件末尾。同样地outputStream.write(buffer, 0, bytesRead)也会阻塞直到所有数据都被写入。 
这个简单的例子演示了BIO的基本工作方式它会阻塞等待I/O操作的完成。在高并发或大数据量的场景下这种阻塞行为可能会成为性能瓶颈这时可能需要考虑使用NIONon-blocking I/O或其他更高效的I/O模型。 
六、NIO实现文件复制 
Java NIO实现文件复制使用FileChannel和ByteBuffer来以流的方式处理文件适合处理大文件因为它不会一次性将整个文件加载到内存中。 
import java.io.IOException;  
import java.nio.ByteBuffer;  
import java.nio.channels.FileChannel;  
import java.nio.file.Files;  
import java.nio.file.Path;  
import java.nio.file.Paths;  
import java.nio.file.StandardOpenOption;  public class FileCopyWithNIO {  public static void main(String[] args) {  // 源文件路径  Path sourcePath  Paths.get(source.txt);  // 目标文件路径  Path destinationPath  Paths.get(destination.txt);  try {  // 打开源文件以进行读取并获取其FileChannel  FileChannel sourceChannel  FileChannel.open(sourcePath, StandardOpenOption.READ);  // 打开或创建目标文件以进行写入并获取其FileChannel  // 注意使用TRY_WITH_RESOURCES需要确保sourceChannel和destinationChannel都实现了AutoCloseable接口  // 这里我们手动关闭它们所以没有使用TRY_WITH_RESOURCES  FileChannel destinationChannel  FileChannel.open(destinationPath, StandardOpenOption.CREATE, StandardOpenOption.WRITE);  // 分配一个ByteBuffer来存储从源文件中读取的数据  ByteBuffer buffer  ByteBuffer.allocate(1024); // 缓冲区大小可以根据需要调整  // 读取并复制文件内容  while (sourceChannel.read(buffer) ! -1) {  // 切换ByteBuffer为读模式准备从buffer中读取数据  buffer.flip();  // 将数据写入目标文件  destinationChannel.write(buffer);  // 清空buffer准备下一次读取  buffer.clear();  }  // 关闭文件通道  sourceChannel.close();  destinationChannel.close();  System.out.println(File copied successfully.);  } catch (IOException e) {  System.err.println(Error occurred while copying file:   e.getMessage());  }  }  
}我们首先指定了源文件和目标文件的路径。使用FileChannel.open()方法打开源文件进行读取并打开或创建目标文件进行写入。分配一个ByteBuffer来作为数据传输的中介。这个缓冲区的大小可以根据需要进行调整在这里我们选择了一个1024字节的缓冲区。使用一个while循环来持续从源文件中读取数据直到文件末尾。在每次循环中使用sourceChannel.read(buffer)方法读取数据到缓冲区。调用buffer.flip()来准备从缓冲区中读取刚刚写入的数据。这一步是必需的因为buffer在写模式后需要切换到读模式。使用destinationChannel.write(buffer)方法将缓冲区中的数据写入目标文件。调用buffer.clear()来清空缓冲区准备下一次读取操作。这一步不会真正清除缓冲区中的数据而是将缓冲区的位置设置为0并将限制设置为容量以便下一次写入操作。关闭源文件和目标文件的通道。 
这个例子展示了如何使用Java NIO的FileChannel和ByteBuffer以高效的方式复制文件特别适用于处理大文件因为它不需要一次性加载整个文件到内存中。 
七、BIO实现socket通信 
使用BIOBlocking I/O阻塞式I/O实现socket通信涉及到ServerSocket和Socket类。下面是一个简单的例子展示如何使用BIO实现一个基本的服务器-客户端socket通信。  
服务器端Server 
import java.io.BufferedReader;  
import java.io.IOException;  
import java.io.InputStreamReader;  
import java.io.PrintWriter;  
import java.net.ServerSocket;  
import java.net.Socket;  public class BIOServer {  public static void main(String[] args) {  int port  8080;  try (ServerSocket serverSocket  new ServerSocket(port)) {  System.out.println(Server is listening on port   port);  while (true) {  Socket clientSocket  serverSocket.accept(); // 阻塞等待客户端连接  System.out.println(Client connected from   clientSocket.getInetAddress());  // 启动一个新线程来处理客户端请求  new Thread(() - handleClient(clientSocket)).start();  }  } catch (IOException e) {  System.err.println(Could not listen on port   port);  e.printStackTrace();  }  }  private static void handleClient(Socket clientSocket) {  try (BufferedReader in  new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));  PrintWriter out  new PrintWriter(clientSocket.getOutputStream(), true)) {  String clientMessage;  while ((clientMessage  in.readLine()) ! null) { // 阻塞等待客户端消息  System.out.println(Received message from client:   clientMessage);  out.println(Echo from server:   clientMessage); // 发送响应给客户端  }  } catch (IOException e) {  System.err.println(Error handling client:   e.getMessage());  } finally {  try {  clientSocket.close();  } catch (IOException e) {  e.printStackTrace();  }  }  }  
}客户端Client 
import java.io.BufferedReader;  
import java.io.IOException;  
import java.io.InputStreamReader;  
import java.io.PrintWriter;  
import java.net.Socket;  public class BIOClient {  public static void main(String[] args) {  String host  localhost;  int port  8080;  try (Socket socket  new Socket(host, port)) {  System.out.println(Connected to server at   host  :  port);  // 发送消息给服务器  PrintWriter out  new PrintWriter(socket.getOutputStream(), true);  BufferedReader in  new BufferedReader(new InputStreamReader(socket.getInputStream()));  BufferedReader stdIn  new BufferedReader(new InputStreamReader(System.in));  String userInput;  while ((userInput  stdIn.readLine()) ! null) {  out.println(userInput);  String serverResponse  in.readLine(); // 阻塞等待服务器响应  System.out.println(Server response:   serverResponse);  if (userInput.equalsIgnoreCase(bye)) {  break;  }  }  } catch (IOException e) {  System.err.println(Couldnt connect to server at   host  :  port);  e.printStackTrace();  }  }  
}在这个例子中服务器使用ServerSocket监听8080端口。当客户端连接时serverSocket.accept()方法会阻塞直到有客户端连接上。一旦连接建立服务器会为新连接的客户端启动一个新线程来处理通信。 
客户端使用Socket类连接到服务器。客户端和服务器都使用BufferedReader和PrintWriter来读写数据。注意在读取和写入数据时这些操作都是阻塞的。 
这个例子展示了BIO的基本工作原理读写操作受到操作系统的控制并且在操作完成之前执行这些操作的线程会被阻塞。在高并发的场景下这种模型可能会导致资源利用率低下因为每个连接都需要一个线程来处理。 
八、NIO实现socket通信 
使用Selector来实现非阻塞式I/O操作我们创建一个简单的非阻塞式服务器它能够同时处理多个客户端连接。  
服务端 
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.Iterator;  
import java.util.Set;  public class NonBlockingServer {  public static void main(String[] args) throws IOException {  // 创建一个Selector  Selector selector  Selector.open();  // 打开一个ServerSocketChannel并设置为非阻塞模式  ServerSocketChannel serverSocketChannel  ServerSocketChannel.open();  serverSocketChannel.configureBlocking(false);  // 绑定ServerSocketChannel到一个地址和端口  serverSocketChannel.bind(new InetSocketAddress(8080));  // 将ServerSocketChannel注册到Selector关心ACCEPT事件  serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);  while (true) {  // 阻塞等待需要处理的事件  int readyChannels  selector.select();  if (readyChannels  0) continue;  // 获取可用通道集合  SetSelectionKey selectedKeys  selector.selectedKeys();  IteratorSelectionKey keyIterator  selectedKeys.iterator();  // 遍历SelectionKey  while (keyIterator.hasNext()) {  SelectionKey key  keyIterator.next();  // 检查是否有新的连接如果有则接受该连接  if (key.isAcceptable()) {  ServerSocketChannel server  (ServerSocketChannel) key.channel();  SocketChannel client  server.accept();  client.configureBlocking(false);  client.register(selector, SelectionKey.OP_READ);  System.out.println(Accepted new connection from   client);  }  // 检查是否有数据可读  else if (key.isReadable()) {  SocketChannel client  (SocketChannel) key.channel();  ByteBuffer buffer  ByteBuffer.allocate(1024);  int bytesRead  client.read(buffer);  if (bytesRead  -1) {  client.close();  } else {  buffer.flip();  while (buffer.hasRemaining()) {  System.out.print((char) buffer.get());  }  client.register(selector, SelectionKey.OP_WRITE);  }  }  // 检查是否有数据可写  else if (key.isWritable()) {  SocketChannel client  (SocketChannel) key.channel();  ByteBuffer buffer  ByteBuffer.allocate(1024);  buffer.put(Hello from server!.getBytes());  buffer.flip();  client.write(buffer);  client.close();  }  // 处理完SelectionKey后需要从集合中删除  keyIterator.remove();  }  }  }  
}我们首先创建了一个Selector实例它将用于监控多个通道的状态。然后我们打开了一个ServerSocketChannel将其设置为非阻塞模式并绑定到指定的端口在这个例子中是8080。我们将ServerSocketChannel注册到Selector并指定我们关心ACCEPT事件这意味着我们想要知道何时有新的连接请求。进入一个无限循环在循环中我们调用selector.select()来阻塞等待直到至少有一个通道准备好进行I/O操作。一旦select()返回我们获取Selector中已选择的键的集合并遍历它们。对于每个键我们检查它是否可接受即是否有新的连接等待被接受、是否可读即是否有数据可以从通道中读取或是否可写即是否有数据可以写入通道。根据键的状态我们执行相应的操作如接受新连接、读取数据或写入数据。在处理完一个键之后我们必须从选定的键集合中删除它否则下次选择操作时它仍会被包含在内。 
客户端将连接到服务器发送一条消息并等待接收服务器的响应 
import java.io.IOException;  
import java.net.InetSocketAddress;  
import java.nio.ByteBuffer;  
import java.nio.channels.SocketChannel;  public class NonBlockingClient {  public static void main(String[] args) throws IOException {  // 打开一个SocketChannel并连接到服务器  SocketChannel socketChannel  SocketChannel.open();  socketChannel.configureBlocking(false);  socketChannel.connect(new InetSocketAddress(localhost, 8080));  // 完成连接过程  while (!socketChannel.finishConnect()) {  // 非阻塞模式下可能需要多次调用finishConnect()来完成连接  System.out.println(Connecting to server...);  }  System.out.println(Connected to server);  // 准备要发送的数据  String message  Hello from client!;  ByteBuffer writeBuffer  ByteBuffer.wrap(message.getBytes());  // 发送数据到服务器  while (writeBuffer.hasRemaining()) {  socketChannel.write(writeBuffer);  }  // 准备读取服务器的响应  ByteBuffer readBuffer  ByteBuffer.allocate(1024);  // 读取服务器的响应  while (true) {  int bytesRead  socketChannel.read(readBuffer);  if (bytesRead  -1) {  break; // 服务器已关闭连接  }  if (bytesRead  0) {  readBuffer.flip();  while (readBuffer.hasRemaining()) {  System.out.print((char) readBuffer.get());  }  readBuffer.clear();  }  }  // 关闭SocketChannel  socketChannel.close();  }  
}我们首先创建一个SocketChannel实例并设置其为非阻塞模式。使用connect()方法异步地连接到服务器。在非阻塞模式下connect()方法可能立即返回而连接过程可能尚未完成。因此我们需要检查连接状态并可能需要多次调用finishConnect()来确保连接已经完成。一旦连接建立我们创建一个包含要发送的消息的ByteBuffer并使用SocketChannel的write()方法发送数据。注意在非阻塞模式下write()方法可能不会发送所有的数据因此我们需要在循环中调用它直到所有数据都被发送。接着我们准备一个空的ByteBuffer来接收服务器的响应并在一个循环中调用read()方法来读取数据。同样地由于我们处于非阻塞模式read()方法可能立即返回0如果没有数据可读或者返回实际读取的字节数。当read()返回-1时表示服务器已经关闭了连接。我们使用flip()方法来准备从ByteBuffer中读取数据并在读取完数据后使用clear()方法来清空缓冲区以便下次读取。最后我们关闭SocketChannel来释放资源。 
九、总结 
Java BIO和NIO是两种不同的I/O模型它们在设计、工作原理和使用上有显著的区别。BIO是同步阻塞的I/O模型它简单直接但性能有限而NIO是同步非阻塞的I/O模型它引入了多路复用器和缓冲区的概念提高了系统的吞吐量和性能。在选择使用哪种模型时需要根据具体的应用场景和需求进行权衡。如果并发连接数较少且对性能要求不高可以选择使用BIO如果并发连接数较多且对性能要求较高可以选择使用NIO。