怎么优化呢?
对于一次I/O访问(以read举例),数据会先被拷贝到操作系统内核的缓冲区,然后才会从操作系统内核的缓冲区拷贝到应用程序的地址空间 。
所以说,当一个read操作发生时,它会经历两个阶段:
- 等待数据准备 (Waiting for the data to be ready) 。
- 将数据从内核拷贝到进程中 (Copying the data from the kernel to the process) 。
02
I/O模型
2.1 阻塞 I/O - Blocking I/O

文章插图
简介:最原始的网络I/O模型 。进程会一直阻塞,直到数据拷贝完成 。
缺点:高并发时,服务端与客户端对等连接,线程多带来的问题:
- CPU资源浪费,上下文切换 。
- 内存成本几何上升,JVM一个线程的成本约1MB 。

文章插图
简介: 进程反复系统调用,并马上返回结果 。
缺点: 当进程有1000fds,代表用户进程轮询发生系统调用1000次kernel,来回的用户态和内核态的切换,成本几何上升 。
public static void main(String[] args) throws IOException { ServerSocketChannel ss = ServerSocketChannel.open(); ss.bind(new InetSocketAddress(Constant.HOST, Constant.PORT)); System.out.println(" NIO server started ... "); ss.configureBlocking(false); int idx =0; while (true) { final SocketChannel socket = ss.accept();//阻塞方法 new Thread(() -> { handle(socket); },"线程["+idx+"]" ).start(); } } static void handle(SocketChannel socket) { try { socket.configureBlocking(false); ByteBuffer byteBuffer = ByteBuffer.allocate(1024); socket.read(byteBuffer); byteBuffer.flip(); System.out.println("请求:" + new String(byteBuffer.array())); String resp = "服务器响应"; byteBuffer.get(resp.getBytes()); socket.write(byteBuffer); } catch (IOException e) { e.printStackTrace(); } }2.3 I/O 多路复用 - IO multiplexing

文章插图
简介: 单个线程就可以同时处理多个网络连接 。内核负责轮询所有socket,当某个socket有数据到达了,就通知用户进程 。多路复用在Linux内核代码迭代过程中依次支持了三种调用,即SELECT、POLL、EPOLL三种多路复用的网络I/O模型 。下文将画图结合Java代码解释 。
2.3.1 I/O 多路复用- select

文章插图
简介: 有连接请求抵达了再检查处理 。
缺点:
- 句柄上限- 默认打开的FD有限制,1024个 。
- 重复初始化-每次调用 select(),需要把 fd 集合从用户态拷贝到内核态,内核进行遍历 。
- 逐个排查所有FD状态效率不高 。
public static void main(String[] args) throws IOException { ServerSocketChannel ssc = ServerSocketChannel.open();//管道型ServerSocket ssc.socket().bind(new InetSocketAddress(Constant.HOST, Constant.PORT)); ssc.configureBlocking(false);//设置非阻塞 System.out.println(" NIO single server started, listening on :" + ssc.getLocalAddress()); Selector selector = Selector.open(); ssc.register(selector, SelectionKey.OP_ACCEPT);//在建立好的管道上,注册关心的事件 就绪 while(true) { selector.select(); Set<SelectionKey> keys = selector.selectedKeys(); Iterator<SelectionKey> it = keys.iterator(); while(it.hasNext()) { SelectionKey key = it.next(); it.remove();//处理的事件,必须删除 handle(key); } } } private static void handle(SelectionKey key) throws IOException { if(key.isAcceptable()) { ServerSocketChannel ssc = (ServerSocketChannel) key.channel(); SocketChannel sc = ssc.accept(); sc.configureBlocking(false);//设置非阻塞 sc.register(key.selector(), SelectionKey.OP_READ );//在建立好的管道上,注册关心的事件 可读 } else if (key.isReadable()) { //flip SocketChannel sc = null; sc = (SocketChannel)key.channel(); ByteBuffer buffer = ByteBuffer.allocate(512); buffer.clear(); int len = sc.read(buffer); if(len != -1) { System.out.println("[" +Thread.currentThread().getName()+"] recv :"+ new String(buffer.array(), 0, len)); } ByteBuffer bufferToWrite = ByteBuffer.wrap("HelloClient".getBytes()); sc.write(bufferToWrite); } }
推荐阅读
- 黄山徽州区,多层面下功夫推进茶业优化升级
- 在淘宝买饰品哪里发货的好 开饰品店从哪进货
- 作为外太空的不速之客,大部分陨石来自月球 人类从宇火星的陨石中发现了什么
- 资源整合促推茶产业 市场给力生发茶财富
- 梦到亲人穿白色衣服 梦见亲人穿白色衣服从旁边走过
- 聚焦农残,从专家的茶文化中解读食品安全问题
- 从网站流量来源如何分析网站推广效果?
- win7如何备份系统
- 从事 Android 开发六年,我学到的那些事
- 从章鱼开始进化 从章鱼开始进化
