简介
Java 支持三种网络 IO 模子:BIO、NIO、AIO。
- Java BIO 是同步阻塞模子,一个连接对应一个线程,客户端有连接请求时服务端就启动一个线程,纵然这个连接不做任何事变也会占用线程资源。
- Java NIO 是同步非阻塞模子,一个线程可以处置惩罚多个连接,客户端连接请求会注册到多路复用器(Selector),多路复用器检测到连接有 IO 时间就会处置惩罚。
- Java AIO 是异步非阻塞模子,AIO 引入了异步通道的概念,读写异步通道会立刻返回,读写的数据由 Future 或 CompletionHandler 进一步处置惩罚。
BIO 实用于连接数少的场景,程序编写比较简单,对服务器的资源要求比较高,JDK1.4之前的唯一选择。NIO 实用于连接数多的场景,例如谈天服务器、服务器间通讯等,程序编写比较复杂,JDK1.4开始支持。AIO 也实用于连接数多的场景,但更加偏向于异步操作多的场景。
Java BIO
模子示例
客户端代码示例
- import java.io.*;
- import java.net.InetSocketAddress;
- import java.net.Socket;
- import java.net.SocketAddress;
- public class BIOClient {
- public static void main(String[] args) {
- new BIOClient().start("localhost", 6666);
- }
- public void start(String host, int port) {
- // 初始化 socket
- Socket socket = new Socket();
- try {
- // 设置 socket 连接
- SocketAddress remote = new InetSocketAddress(host, port);
- socket.setSoTimeout(5000);
- socket.connect(remote);
- // 发送数据
- PrintWriter writer = getWriter(socket);
- writer.write("hello server");
- writer.flush();
- // // 发起请求
- // PrintWriter writer = getWriter(socket);
- // writer.write(compositeRequest(host));
- // writer.flush();
- //
- // // 读取响应
- // String msg;
- // BufferedReader reader = getReader(socket);
- // while ((msg = reader.readLine()) != null) {
- // System.out.println(msg);
- // }
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- try {
- socket.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- private BufferedReader getReader(Socket socket) throws IOException {
- InputStream in = socket.getInputStream();
- return new BufferedReader(new InputStreamReader(in));
- }
- private PrintWriter getWriter(Socket socket) throws IOException {
- OutputStream out = socket.getOutputStream();
- return new PrintWriter(new OutputStreamWriter(out));
- }
- private String compositeRequest(String host) {
- return "GET / HTTP/1.1\r\n" +
- "Host: " + host + "\r\n" +
- "User-Agent: curl/7.43.0\r\n" +
- "Accept: */*\r\n\r\n";
- }
- }
复制代码 服务端代码示例
- import java.io.InputStream;
- import java.net.ServerSocket;
- import java.net.Socket;
- import java.util.concurrent.ExecutorService;
- import java.util.concurrent.Executors;
- public class BIOServer {
- public static void main(String[] args) throws Exception {
- // 创建一个线程池
- ExecutorService pool = Executors.newCachedThreadPool();
- // 创建 ServerSocket
- ServerSocket serverSocket = new ServerSocket(6666);
- while (true) {
- // 等待客户端连接
- final Socket socket = serverSocket.accept();
- // 接收到一个客户端连接 放入线程池进行处理
- pool.execute(() -> process(socket));
- }
- }
- static void process(Socket socket) {
- try {
- byte[] bytes = new byte[1024];
- // 通过 socket 获取输入流
- InputStream inputStream = socket.getInputStream();
- // 循环读取客户端发送的数据
- while (true) {
- // 没有数据的时候这里会阻塞等待
- int read = inputStream.read(bytes);
- if (read == -1) break;
- // 输出客户端发送的数据
- System.out.println(new String(bytes, 0, read));
- }
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- try {
- socket.close();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
- }
复制代码 Java NIO
NIO 接纳 Reactor 模式,属于 IO 多路复用模子,可以用一个线程处置惩罚多个请求。NIO 有三大核心模块,通道(Channel)、缓冲区(Buffer)、选择器(Selector)。NIO 的非阻塞模式,使主线程在未发生数据读写事件时无需阻塞,可以继承做其他事变,这就大大增强了服务器的并发处置惩罚能力。
模子示例
Selector 对应一个线程,一个 Selector 可以对应多个 Channel,一个 Channel 对应一个 Buffer。程序切换到哪个 Channel 是由事件决定的,Selector 会根据不同的事件切换不同的 Channel。下图描述了 Channel、Buffer 和 Selector 的关系。
MappedByteBuffer 简介
NIO 提供的 MappedByteBuffer 支持支持在内存(堆外内存)中修改文件,可以淘汰一次数据拷贝。文件同步的部分,由 NIO 本身完成。
代码示例
- import java.io.RandomAccessFile;
- import java.nio.MappedByteBuffer;
- import java.nio.channels.FileChannel;
- /**
- * 说明 1.MappedByteBuffer 可让文件直接在内存(堆外内存)修改,操作系统不需要拷贝一次
- */
- public class MappedByteBufferTest {
- public static void main(String[] args) throws Exception {
- RandomAccessFile randomAccessFile = new RandomAccessFile("1.txt", "rw");
- //获取对应的通道
- FileChannel channel = randomAccessFile.getChannel();
- /**
- * 参数 1:FileChannel.MapMode.READ_WRITE 使用的读写模式
- * 参数 2:0:可以直接修改的起始位置
- * 参数 3:5: 是映射到内存的大小(不是索引位置),即将 1.txt 的多少个字节映射到内存
- * 可以直接修改的范围就是 0-5
- * 实际类型 DirectByteBuffer
- */
- MappedByteBuffer mappedByteBuffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, 5);
- mappedByteBuffer.put(0, (byte) 'H');
- mappedByteBuffer.put(3, (byte) '9');
- mappedByteBuffer.put(5, (byte) 'Y');//IndexOutOfBoundsException
- randomAccessFile.close();
- System.out.println("修改成功~~");
- }
- }
复制代码 NIO 编程代码原理分析图
关于 NIO 非阻塞网络编程相关的(Selector、SelectionKey、ServerScoketChannel 和 SocketChannel)关系梳理图
服务端代码示例
可以结合上面的原理图观察代码实现细节
Java AIO
AIO 是异步非阻塞的,引入了异步通道的概念,接纳 Proactor 模式,操作系统完成数据拷贝操作后才会关照服务端线程。AIO 本质上还是 IO 多路复用模子,与 NIO 比起来,AIO 只是在非阻塞的前提下增加了异步功能,详细则表现在代码编写以及数据传输两个层面。
- 从代码编写角度来说,原来的同步方法会阻塞等候接口返回,而现在可以异步等候返回结果。
- 从数据传输角度来说,每个请求都须要传输数据,NIO 固然好坏阻塞的,但是事件到达后,NIO 须要本身把数据从内核空间复制到用户空间。AIO 引入异步逻辑后,事件到达后系统不会立刻关照服务端线程,而是会本身把数据从内核空间复制到用户空间,完成这个操作后,才会关照服务端线程行止理。
AIO 的使用场景还是比较少,现在大部分开源框架中应该还是以使用 NIO 为主,AIO 在性能方面的提升还是比较有限,紧张的变化还是增加了异步功能。
如何明白 Reactor 和 Proactor 的区别?
Reactor 可以明白为「来了事件操作系统关照应用进程,让应用进程来处置惩罚」,而Proactor 可以明白为「来了事件操作系统来处置惩罚,处置惩罚完再关照应用进程」。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |