Java|从操作系统层面分析 Java IO 演进之路

Java|从操作系统层面分析 Java IO 演进之路



Java|从操作系统层面分析 Java IO 演进之路



Java|从操作系统层面分析 Java IO 演进之路






前言 本文从操作系统实际调用角度(以CentOS Linux release 7.5操作系统为示例) , 力求追根溯源看IO的每一步操作到底发生了什么 。
关于如何查看系统调用 , Linux可以使用 strace 来查看任何软件的系统调动(这是个很好的分析学习方法):strace -ff -o ./out java TestJava
一 BIO /** * Alipay.com Inc. Copyright (c) 2004-2020 All Rights Reserved. */package io; import java.io.*;import java.net.ServerSocket;import java.net.Socket;/** * @author xiangyong.ding * @version $Id: TestSocket.java v 0.1 2020年08月02日 20:56 xiangyong.ding Exp $ */public class BIOSocket { public static void main(String[
args) throws IOException { ServerSocket serverSocket = new ServerSocket(8090); System.out.println(\"step1: new ServerSocket \"); while (true) { Socket client = serverSocket.accept(); System.out.println(\"step2: client\\t\" + client.getPort()); new Thread(() -{ try { InputStream in = client.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(in)); while (true) { System.out.println(reader.readLine());catch (IOException e) { e.printStackTrace();).start();1 发生的系统调用
启动时
socket(AF_INET SOCK_STREAM IPPROTO_IP) = 5bind(5 {sa_family=AF_INET sin_port=htons(8090) sin_addr=inet_addr(\"0.0.0.0\") 16) = 0listen(5 50) = 0poll([{fd=5 events=POLLIN|POLLERR
1 -1) = 1 ([{fd=5 revents=POLLIN
) poll函数会阻塞直到其中任何一个fd发生事件 。
有客户端连接后
accept(5 {sa_family=AF_INET sin_port=htons(10253) sin_addr=inet_addr(\"42.120.74.252\") [16
) = 6clone(child_stack=0x7f013e5c4fb0 flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID parent_tidptr=0x7f013e5c59d0 tls=0x7f013e5c5700 child_tidptr=0x7f013e5c59d0) = 13168poll([{fd=5 events=POLLIN|POLLERR
1 -1 抛出线程(即我们代码里的 new Thread() )后 , 继续poll阻塞等待连接 。
clone出来的线程
recvfrom(6 \"hellobio\\" 8192 0 NULL NULL) = 关于对recvfrom函数的说明 , 其中第四个参数0 表示这是一个阻塞调用 。
客户端发送数据后
recvfrom(6 \"hellobio\\" 8192 0 NULL NULL) = 10 2 优缺点
优点
代码简单 , 逻辑清晰 。
缺点
由于stream的read操作是阻塞读 , 面对多个连接时 每个连接需要每线程 。 无法处理大量连接(C10K问题) 。 误区:可见JDK1.8中对于最初的BIO , 在Linux OS下仍然使用的poll , poll本身也是相对比较高效的多路复用函数(支持非阻塞、多个socket同时检查event) , 只是限于JDK最初的stream API限制 , 无法支持非阻塞读取 。 二 NIO(non block) 改进:使用NIO API , 将阻塞变为非阻塞 ,不需要大量线程 。
/** * Alipay.com Inc. Copyright (c) 2004-2020 All Rights Reserved. */package io;import java.io.IOException;import java.net.InetSocketAddress;import java.nio.ByteBuffer;import java.nio.channels.ServerSocketChannel;import java.nio.channels.SocketChannel;import java.util.LinkedList;/** * @author xiangyong.ding * @version $Id: NioSocket.java v 0.1 2020年08月09日 11:25 xiangyong.ding Exp $ */public class NIOSocket { private static LinkedListSocketChannelclients = new LinkedList(); private static void startClientChannelHandleThread(){ new Thread(() -{ while (true){ ByteBuffer buffer = ByteBuffer.allocateDirect(4096); //处理客户端连接 for (SocketChannel c : clients) { // 非阻塞0 表示读取到的字节数量 0或-1表示未读取到或读取异常 int num = 0; try { num = c.read(buffer);catch (IOException e) { e.printStackTrace();if (num0) { buffer.flip(); byte[