知识大全 IO的阻塞与非阻塞、同步与异步以及Java网络IO交互方式
Posted 操作
篇首语:讨厌自己明明不甘平凡,却又不好好努力。本文由小常识网(cha138.com)小编为大家整理,主要介绍了知识大全 IO的阻塞与非阻塞、同步与异步以及Java网络IO交互方式相关的知识,希望对你有一定的参考价值。
IO的阻塞与非阻塞、同步与异步以及Java网络IO交互方式 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!
最近工作中 接触到了Java网络编程方面的东西 Socket NIO MongoDB等 也看了tomcat的源码 也加强了线程方面的知识 也使用了MINA这样的框架 感觉获益良多 原本技术上的薄弱环节也在慢慢提高 很多想写的东西 也在慢慢规划整理 无奈最近在筹备婚礼的事情 显得有些耽搁
想了很久 决定先写写IO中经常被提到的概念—— 同步与异步 阻塞与非阻塞 以及在Java网络编程中的简单运用
想达到的目的有两个
深入的理解同步与异步 阻塞与非阻塞 这看似烂大街的词汇很多人已经习惯不停的说 但却说不出其中的所以然 包括我
理解各种IO模型在Java网络IO中的运用 能够根据不同的应用场景选择合适的交互方式 了解不同的交互方式对IO性能的影响
前提
首先先强调上下文 下面提到了同步与异步 阻塞与非阻塞的概念都是在IO的场合下 它们在其它场合下有着不同的含义 比如操作系统中 通信技术上
然后借鉴下《Unix网络编程卷》中的理论
IO操作中涉及的 个主要对象为程序进程 系统内核 以读操作为例 当一个IO读操作发生时 通常经历两个步骤
等待数据准备
将数据从系统内核拷贝到操作进程中
例如 在socket上的读操作 步骤 会等到网络数据包到达 到达后会拷贝到系统内核的缓冲区 步骤 会将数据包从内核缓冲区拷贝到程序进程的缓冲区中
阻塞(blocking)与非阻塞(non blocking)IO
IO的阻塞 非阻塞主要表现在一个IO操作过程中 如果有些操作很慢 比如读操作时需要准备数据 那么当前IO进程是否等待操作完成 还是得知暂时不能操作后先去做别的事情?一直等待下去 什么事也不做直到完成 这就是阻塞 抽空做些别的事情 这是非阻塞
非阻塞IO会在发出IO请求后立即得到回应 即使数据包没有准备好 也会返回一个错误标识 使得操作进程不会阻塞在那里 操作进程会通过多次请求的方式直到数据准备好 返回成功的标识
想象一下下面两种场景
A 小明和小刚两个人都很耿直内向 一天小明来找小刚借书 小刚啊 你那本XXX借我看看 于是小刚就去找书 小明就等著 找了半天找到了 把书给了小明
B 小明和小刚两个人都很活泼外向 一天小明来找小刚借书 嘿小刚 你那本XXX借我看看 小刚说 我得找一会 小明就去打球去了 过会又来 这次书找到了 把书给了小明
结论 A是阻塞的 B是非阻塞的
从CPU角度可以看出非阻塞明显提高了CPU的利用率 进程不会一直在那等待 但是同样也带来了线程切换的增加 增加的 CPU 使用时间能不能补偿系统的切换成本需要好好评估
同步(synchronous)与异步(asynchronous)IO
先来看看正式点的定义 POSIX标准将IO模型分为了两种 同步IO和异步IO Richard Stevens在《Unix网络编程卷》中也总结道
A synchronous I/O operation causes the requesting process to be blocked until that I/O operation pletes;An asynchronous I/O operation does not cause the requesting process to be blocked;
可以看出 判断同步和异步的标准在于 一个IO操作直到完成 是否导致程序进程的阻塞 如果阻塞就是同步的 没有阻塞就是异步的 这里的IO操作指的是真实的IO操作 也就是数据从内核拷贝到系统进程(读)的过程
继续前面借书的例子 异步借书是这样的
C 小明很懒 一天小明来找小刚借书 嘿小刚 你那本XXX借我看看 小刚说 我得找一会 小明就出去打球了并且让小刚如果找到了就把书拿给他 小刚是个负责任的人 找到了书送到了小明手上
A和B的借书方式都是同步的 有人要问了B不是非阻塞嘛 怎么还是同步?
前面说了IO操作的 个步骤 准备数据和把数据从内核中拷贝到程序进程 映射到这个例子 书即是准备的数据 小刚是内核 小明是程序进程 小刚把书给小明这是拷贝数据 在B方式中 小刚找书这段时间小明的确是没闲著 该干嘛干嘛 但是小刚找到书把书给小明的这个过程也就是拷贝数据这个步骤 小明还是得乖乖的回来候着小刚把书递手上 所以这里就阻塞了 根据上面的定义 所以是同步
在涉及到 IO 处理时通常都会遇到一个是同步还是异步的处理方式的选择问题 同步能够保证程序的可靠性 而异步可以提升程序的性能 小明自己去取书不管等著不等著迟早拿到书 指望小刚找到了送来 万一小刚忘了或者有急事忙别的了 那书就没了
讨论
说实话 网上关于同步与异步 阻塞与非阻塞的文章多之又多 大部分是拷贝的 也有些写的非常好的 参考了许多 也借鉴了许多 也经过自己的思考
同步与异步 阻塞与非阻塞之间确实有很多相似的地方 很容易混淆 wiki更是把异步与非阻塞画上了等号 更多的人还是认为他们是不同的 原因可能有很多 每个人的知识背景不同 设定的上下文也不同
我的看法是 在IO中 根据上面同步异步的概念 也可以看出来同步与异步往往是通过阻塞非阻塞的形式来表达的 并且是通过一种中间处理机制来达到异步的效果 同步与异步往往是IO操作请求者和回应者之间在IO实际操作阶段的协作方式 而阻塞非阻塞更确切的说是一种自身状态 当前进程或者线程的状态
在发出IO读请求后 阻塞IO会一直等待有数据可读 当有数据可读时 会等待数据从内核拷贝至系统进程 而非阻塞IO都会立即返回 至于数据怎么处理是程序进程自己的事情 无关同步和异步
两种方式的组合
组合的方式当然有四种 分别是 同步阻塞 同步非阻塞 异步阻塞 异步非阻塞
Java网络IO实现和IO模型
不同的操作系统上有不同的IO模型 《Unix网络编程卷》将unix上的IO模型分为 类 blocking I/O nonblocking I/O I/O multiplexing (select and poll) signal driven I/O (SIGIO)以及asynchronous I/O (the POSIX aio_functions) 具体可参考《Unix网络编程卷 》 章节
在windows上IO模型也是有 种 select WSAAsyncSelect WSAEventSelect Overlapped I/O 事件通知以及IOCP 具体可参考windows五种IO模型 Java是平台无关的语言 在不同的平台上会调用底层操作系统的不同的IO实现 下面就来说一下Java提供的网络IO的工具和实现 为了扩大阻塞非阻塞的直观感受 我都使用了长连接
阻塞IO
同步阻塞最常用的一种用法 使用也是最简单的 但是 I/O 性能一般很差 CPU 大部分在空闲状态 下面是一个简单的基于TCP的同步阻塞的Socket服务端例子
@Test public void testJIoSocket() throws Exception ServerSocket serverSocket = new ServerSocket( ); Socket socket = null; try while (true) socket = serverSocket accept(); System out println( socket连接 + socket getRemoteSocketAddress() toString()); BufferedReader in = new BufferedReader(new InputStreamReader(socket getInputStream())); while(true) String readLine = in readLine(); System out println( 收到消息 + readLine); if( end equals(readLine)) break; //客户端断开连接 socket sendUrgentData( xFF); catch (SocketException se) System out println( 客户端断开连接 ); catch (IOException e) e printStackTrace(); finally System out println( socket关闭 + socket getRemoteSocketAddress() toString()); socket close(); 使用SocketTest作为客户端工具进行测试 同时开启 个客户端连接Server端并发送消息 如下图
再看下后台的打印 socket连接 / : 收到消息hello!收到消息my name is client 由于服务器端是单线程的 在第一个连接的客户端阻塞了线程后 第二个客户端必须等待第一个断开后才能连接 当输入 end 字符串断开客户端 这时候看到后台继续打印 socket连接 / : 收到消息hello!收到消息my name is client 收到消息endsocket关闭 / : socket连接 / : 收到消息hello!收到消息my name is client 所有的客户端连接在请求服务端时都会阻塞住 等待前面的完成 即使是使用短连接 数据在写入 OutputStream 或者从 InputStream 读取时都有可能会阻塞 这在大规模的访问量或者系统对性能有要求的时候是不能接受的 阻塞IO + 每个请求创建线程/线程池 通常解决这个问题的方法是使用多线程技术 一个客户端一个处理线程 出现阻塞时只是一个线程阻塞而不会影响其它线程工作 为了减少系统线程的开销 采用线程池的办法来减少线程创建和回收的成本 模式如下图 简单的实现例子如下 使用一个线程(Accptor)接收客户端请求 为每个客户端新建线程进行处理(Processor) 线程池的我就不弄了 public class MultithreadJIoSocketTest @Test public void testMultithreadJIoSocket() throws Exception ServerSocket serverSocket = new ServerSocket( ) Thread thread = new Thread(new Accptor(serverSocket)) thread start() Scanner scanner = new Scanner(System in) scanner next() public class Accptor implements Runnable private ServerSocket serverSocket; public Accptor(ServerSocket serverSocket) this serverSocket = serverSocket; public void run() while (true) Socket socket = null; try socket = serverSocket accept() if(socket != null) System out println( 收到了socket: + socket getRemoteSocketAddress() toString()) Thread thread = new Thread(new Processor(socket)) thread start() catch (IOException e) e printStackTrace() public class Processor implements Runnable private Socket socket; public Processor(Socket socket) this socket = socket; @Override public void run() try BufferedReader in = new BufferedReader(new InputStreamReader(socket getInputStream())) String readLine; while(true) readLine = in readLine() System out println( 收到消息 + readLine) if( end equals(readLine)) break; //客户端断开连接 socket sendUrgentData( xFF) Thread sleep( ) catch (InterruptedException e) e printStackTrace() catch (SocketException se) System out println( 客户端断开连接 ) catch (IOException e) e printStackTrace() finally try socket close() catch (IOException e) e printStackTrace() 使用 个客户端连接 这次没有阻塞 成功的收到了 个客户端的消息 收到了socket:/ : 收到了socket:/ : 收到消息hello!收到消息hello! 在单个线程处理中 我人为的使单个线程read后阻塞 秒 就像前面说的 出现阻塞也只是在单个线程中 没有影响到另一个客户端的处理 这种阻塞IO的解决方案在大部分情况下是适用的 在出现NIO之前是最通常的解决方案 Tomcat里阻塞IO的实现就是这种方式 但是如果是大量的长连接请求呢?不可能创建几百万个线程保持连接 再退一步 就算线程数不是问题 如果这些线程都需要访问服务端的某些竞争资源 势必需要进行同步操作 这本身就是得不偿失的 非阻塞IO + IO multiplexing Java从 开始提供了NIO工具包 这是一种不同于传统流IO的新的IO方式 使得Java开始对非阻塞IO支持 NIO并不等同于非阻塞IO 只要设置Blocking属性就可以控制阻塞非阻塞 至于NIO的工作方式特点原理这里一概不说 以后会写 模式如下图 下面是简单的实现 public class NioNonBlockingSelectorTest Selector selector; private ByteBuffer receivebuffer = ByteBuffer allocate( ) @Test public void testNioNonBlockingSelector() throws Exception selector = Selector open() SocketAddress address = new InetSocketAddress( ) ServerSocketChannel channel = ServerSocketChannel open() channel socket() bind(address) nfigureBlocking(false) channel register(selector SelectionKey OP_ACCEPT) while(true) selector select() Iterator iterator = selector selectedKeys() iterator() while (iterator hasNext()) SelectionKey selectionKey = iterator next() iterator remove() handleKey(selectionKey) private void handleKey(SelectionKey selectionKey) throws IOException ServerSocketChannel server = null; SocketChannel client = null; if(selectionKey isAcceptable()) server = (ServerSocketChannel)selectionKey channel() client = server accept() System out println( 客户端 + client socket() getRemoteSocketAddress() toString()) nfigureBlocking(false) client register(selector SelectionKey OP_READ) if(selectionKey isReadable()) client = (SocketChannel)selectionKey channel() receivebuffer clear() int count = client read(receivebuffer) if (count > ) String receiveText = new String( receivebuffer array() count) System out println( 服务器端接受客户端数据 : + receiveText) client register(selector SelectionKey OP_READ) Java NIO提供的非阻塞IO并不是单纯的非阻塞IO模式 而是建立在Reactor模式上的IO复用模型 在IO multiplexing Model中 对于每一个socket 一般都设置成为non blocking 但是整个用户进程其实是一直被阻塞的 只不过进程是被select这个函数阻塞 而不是被socket IO给阻塞 所以还是属于非阻塞的IO 这篇文章中把这种模式归为了异步阻塞 我其实是认为这是同步非阻塞的 可能看的角度不一样 异步IO Java 中提供了异步IO的支持 暂时还没有看过 所以以后再讨论 网络IO优化 对于网络IO有一些基本的处理规则如下 减少交互的次数 比如增加缓存 合并请求 减少传输数据大小 比如压缩后传输 约定合理的数据协议 减少编码 比如提前将字符转化为字节再传输 根据应用场景选择合适的交互方式 同步阻塞 同步非阻塞 异步阻塞 异步非阻塞 就说到这里吧 感觉有点乱 有些地方还是找不到更贴切的语言来描述 cha138/Article/program/Java/hx/201311/26001
相关参考
Java网络编程从入门到精通(31):非阻塞I/O简介 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧
Java网络编程从入门到精通(32):一个非阻塞I/O的例子 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看
Java网络编程从入门到精通(33):非阻塞I/O的缓冲区(Buffer) 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我
首先了解下所谓的javanio是个什么东西! 传统的并发型服务器设计是利用阻塞型网络I/O以多线程的模式来实现的然而由 于系统常常在进行网络读写时处于阻塞状态会大大影响系统的性能自Java开始
JavaNIO(异步IO)Socket通信例子 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! /
Java阻塞队列实例分析与破解 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  
检测解决SQLServer延迟阻塞I/O问题 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! 摘要
Java多线程如何防止主线的阻塞 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! Java多线程在
用Java多线程实现无阻塞读取远程文件 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! 我是不怎么
Java的文件IO机制 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! Java有一个庞大的I/O