Java IO全面
转载请注明原文地址:https://www.cnblogs.com/ygj0930/p/10857412.html
一:IO流梳理——字符流、字节流、输入流、输出流
见另一篇博文:https://www.cnblogs.com/ygj0930/p/5827509.html
二:同步&异步、阻塞&非阻塞的概念
同步:同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回。也就是必须一件一件事做,等前一件做完了才能做下一件事。
异步:当一个异步过程调用发出后,调用者不能立刻得到结果,可以先做其他事。当这个调用在完成后,通过状态、通知和回调来通知调用者。
同步与异步 是 调用与结果 的关系。
阻塞:阻塞调用是指调用结果返回之前,当前线程会被挂起(挂起:线程进入非可执行状态,在这个状态下,cpu不会给线程分配时间片,即线程暂停运行)。
同步 不等于 阻塞:对于同步调用来说,很多时候当前线程还是激活的,只是从逻辑上当前函数没有返回,它还是会抢占cpu去执行其他逻辑,也会主动检测io是否准备好。
非阻塞:调用函数时,在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回,在之后通过select通知调用者。
阻塞与非阻塞 是 调用与线程状态 的关系。
三:Linux的5 种 IO 模型
1)阻塞I/O(blocking I/O)
应用程序调用一个IO函数,导致应用程序阻塞,等待数据准备好。 如果数据没有准备好,一直等待….直到数据准备好了,从内核拷贝到用户空间,IO函数返回成功指示。
2)非阻塞I/O (nonblocking I/O)
非阻塞IO通过进程反复调用IO函数(多次系统调用,并马上返回),而在IO过程中,进程是阻塞的。
我们把一个SOCKET接口设置为非阻塞的意思就是:告诉内核,当所请求的I/O操作无法完成时,不要将进程睡眠,而是返回一个错误。这样我们的I/O操作函数将不断的测试数据是否已经准备好,如果没有准备好,继续测试,直到数据准备好为止。在这个不断测试的过程中,会大量的占用CPU的时间。
3) I/O复用(select 和poll) (I/O multiplexing)
能实现同时对多个IO端口进行监听; I/O复用模型会用到select、poll、epoll函数,这几个函数也会使进程阻塞,但是和阻塞I/O所不同的的,这两个函数可以同时阻塞多个I/O操作。而且可以同时对多个读操作,多个写操作的I/O函数进行检测,直到有数据可读或可写时,才真正调用I/O操作函数。
4)信号驱动I/O (signal driven I/O (SIGIO))
首先我们允许进程进行信号驱动I/O,并定义一个信号处理函数;
当数据准备好时,进程会收到一个SIGIO信号,可以在信号处理函数中调用I/O操作函数处理数据。
以上四种其实都是同步IO。
5)异步I/O (asynchronous I/O (the POSIX aio_functions))
当一个异步过程调用发出后,调用者不能立刻得到结果,它转身去做其他事情;
被调用者在执行完毕后,通过状态、通知、回调等信息,通知调用者做完了,然后调用者再接着之前的工作往下进行。

三:BIO、NIO 和 AIO 的区别
BIO:同步并阻塞IO,一个连接一个线程。
NIO:轮询,通道有请求就调用对应处理函数。
“多路复用IO+同步非阻塞IO”,一个单线程Selector阻塞轮询,找到有数据的channel进行非阻塞IO。
NIO是基于事件驱动模式的IO,Reactor模式是事件驱动模式的实现,主要原理见:https://www.jianshu.com/p/eef7ebe28673。
Selector可以轮询多个Channel,因为JDK使用了epoll()代替传统的select实现,没有最大连接句柄限制,所以只需要一个线程负责Selector的轮询,就可以接入成千上万的客户端。
应用程序向selector注册一个channel,由selector不断轮询,如果发现有某个channel发生连接请求,就会通知相应的处理线程。
AIO:订阅,操作系统有IO就通知程序处理。
AIO框架在windows下使用windows IOCP技术,在Linux下使用epoll多路复用IO技术模拟异步IO。
应用程序向操作系统注册IO监听,然后继续做自己的事情。操作系统发生IO事件,并且准备好数据后,在主动通知应用程序,触发相应的函数。
四:Netty框架
1、Java原生NIO存在的问题
1)NIO 的类库和 API 繁杂,使用麻烦:你需要熟练掌握 Selector、ServerSocketChannel、SocketChannel、ByteBuffer 等。
2)需要具备其他的额外技能做铺垫:例如熟悉 Java 多线程编程,因为 NIO 编程涉及到 Reactor 模式,你必须对多线程和网路编程非常熟悉,才能编写出高质量的 NIO 程序。
3)可靠性能力补齐,开发工作量和难度都非常大:例如客户端面临断连重连、网络闪断、半包读写、失败缓存、网络拥塞和异常码流的处理等等。NIO 编程的特点是功能开发相对容易,但是可靠性能力补齐工作量和难度都非常大。
4)JDK NIO 的 Bug:例如臭名昭著的 Epoll Bug,它会导致 Selector 空轮询,最终导致 CPU 100%。官方声称在 JDK 1.6 版本的 update 18 修复了该问题,但是直到 JDK 1.7 版本该问题仍旧存在,只不过该 Bug 发生概率降低了一些而已,它并没有被根本解决。
2、Netty适用场景
Netty 对 JDK 自带的 NIO 的 API 进行了封装,主要使用场景如下:
1)互联网行业:在分布式系统中,各个节点之间需要远程服务调用,高性能的 RPC 框架必不可少,Netty 作为异步高性能的通信框架,往往作为基础通信组件被这些 RPC 框架使用。
典型的应用有:阿里分布式服务框架 Dubbo 的 RPC 框架使用 Dubbo 协议进行节点间通信,Dubbo 协议默认使用 Netty 作为基础通信组件,用于实现各进程节点之间的内部通信。
2)大数据领域:经典的 Hadoop 的高性能通信和序列化组件 Avro 的 RPC 框架,默认采用 Netty 进行跨界点通信,它的 Netty Service 基于 Netty 框架二次封装实现。
3、Netty的高性能设计
Netty 是异步事件驱动的,高性能之处主要来自于其 I/O 模型和线程处理模型,前者决定如何收发数据,后者决定如何处理数据。
1)基于 I/O多路复用模式
Netty 的非阻塞 I/O 的实现关键是基于 I/O 复用模型:

2)面向Buffer的数据读写
传统的 I/O 是面向字节流或字符流的,以流式的方式顺序地从一个 Stream 中读取一个或多个字节, 因此也就不能随意改变读取指针的位置。
在 NIO 中,抛弃了传统的 I/O 流,而是引入了 Channel 和 Buffer 的概念L:在 NIO 中,只能从 Channel 中读取数据到 Buffer 中或将数据从 Buffer 中写入到 Channel,可以随意地读取任意位置的数据。
3)事件驱动的线程模型
发生事件时,主线程把事件放入事件队列。
在另外的线程中不断循环消费事件列表中的事件,调用事件对应的处理逻辑处理事件。
事件驱动方式也被称为消息通知方式,其实是设计模式中观察者模式的实现。

包括 4 个基本组件:
1)事件队列(event queue):接收事件的入口,存储待处理事件;
2)分发器(event mediator):将不同的事件分发到不同通道;
3)事件通道(event channel):分发器与业务处理器之间的联系渠道;
4)事件处理器(event processor):实现业务逻辑,处理完成后会发出事件,触发下一步操作。
此模式的优点:
1)可扩展性好:基于分布式的异步架构,事件与处理器之间高度解耦,可以方便扩展事件处理逻辑;
2)高性能:基于队列暂存事件,能方便并行异步处理事件。
NIO的线程模型——Reactor模型(反应堆模型)
服务端程序处理传入多路请求,并将它们同步分派给请求对应的处理线程.
Reactor 模式也叫 Dispatcher 模式,即 I/O 多路复用统一监听事件,收到事件后分发(Dispatch 给某进程),是编写高性能网络服务器的必备技术之一。
Reactor 模型中有 2 个关键组成:
1)Reactor:Reactor 在一个单独的线程中运行,负责监听和分发事件,分发给适当的处理程序来对 IO 事件做出反应。
2)Handlers:处理程序执行 I/O 事件要完成的实际事件,Reactor 通过调度适当的处理程序来响应 I/O 事件,处理程序执行非阻塞操作。
Netty的线程模型——基于NIO的主从 Reactors 多线程模型作进一步修改
a)MainReactor 负责客户端的连接请求,并将请求转交给 SubReactor;
b)SubReactor 负责相应通道的 IO 读写请求;
c)非 IO 请求(具体逻辑处理)的任务则会直接写入队列,等待 worker threads 进行处理。
4)基于异步的事件处理方式
Netty 中的 I/O 操作是异步的,包括 Bind、Write、Connect 等操作会简单的返回一个 ChannelFuture。
调用者并不能立刻获得结果,而是通过 Future-Listener 机制,用户可以方便的主动获取或者通过通知机制获得 IO 操作结果。
当 Future 对象刚刚创建时,处于非完成状态,调用者可以通过返回的 ChannelFuture 来获取操作执行的状态,注册监听函数来执行完成后的操作。
Java IO全面的更多相关文章
- java.IO输入输出流:过滤流:buffer流和data流
java.io使用了适配器模式装饰模式等设计模式来解决字符流的套接和输入输出问题. 字节流只能一次处理一个字节,为了更方便的操作数据,便加入了套接流. 问题引入:缓冲流为什么比普通的文件字节流效率高? ...
- Java:IO流与文件基础
Java:IO流与文件基础 说明: 本章内容将会持续更新,大家可以关注一下并给我提供建议,谢谢啦. 走进流 什么是流 流:从源到目的地的字节的有序序列. 在Java中,可以从其中读取一个字节序列的对象 ...
- Java IO之字符流和文件
前面的博文介绍了字节流,那字符流又是什么流?从字面意思上看,字节流是面向字节的流,字符流是针对unicode编码的字符流,字符的单位一般比字节大,字节可以处理任何数据类型,通常在处理文本文件内容时,字 ...
- java Io流向指定文件输入内容
package com.hp.io; import java.io.*; public class BufferedWriterTest{ public static void main(String ...
- java Io文件输入输出流 复制文件
package com.hp.io; import java.io.FileInputStream; import java.io.FileNotFoundException; import java ...
- java Io流更新文件内容
package com.hp.io; import java.io.FileOutputStream; import java.io.IOException; public class FileOut ...
- java IO流详解
流的概念和作用 学习Java IO,不得不提到的就是JavaIO流. 流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象.即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输 ...
- java.io.NotSerializableException: test.io.file.Student
java.io.NotSerializableException: test.io.file.Student at java.io.ObjectOutputStream.writeObject0 ...
- java.io.IOException: mark/reset not supported
java.io.IOException: mark/reset not supported at java.io.InputStream.reset(InputStream.java:348) at ...
- Java IO流学习总结
Java流操作有关的类或接口: Java流类图结构: 流的概念和作用 流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象.即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输 ...
随机推荐
- spring框架的定时任务cronExpression表达式详解
附:cronExpression表达式解释: 0 0 12 * * ?---------------在每天中午12:00触发 0 15 10 ? * *---------------每天上午10:15 ...
- AcWing 91. 最短Hamilton路径
今天第一次在\(AcWing\)这个网站上做题,来发一下此网站的第一篇题解 传送门 思路 直接枚举的话时间复杂度为\(O(n*n!)\) 复杂度显然爆炸,所以我们用二进制枚举,这样就可以把复杂度降到\ ...
- github git clone ssh协议 clone超慢解决方案,提高Github Clone速度
即使进行了fq吧但是git clone ssh协议就是慢 2kb/s你能忍,坚决不能忍. github git clone ssh协议 clone超慢解决方案 151.101.72.249 globa ...
- Consul 使用手册(感觉比较全了)
HTTP API consul的主要接口是RESTful HTTP API,该API可以用来增删查改nodes.services.checks.configguration.所有的endpoints主 ...
- 2018-2019-2 20162329 《网络对抗技术》Exp8: Web基础
目录 Exp8: Web基础 一. 基础问题回答 1. 什么是表单 2. 浏览器可以解析运行什么语言. 3. WebServer支持哪些动态语言 二. 实验过程 1. Web前端HTML 2..Web ...
- 《Linux就该这么学》培训笔记_ch02_一些必须掌握的Linux命令
本文在原来作者的基础上做一些符合自己的修改.原文参考: <Linux就该这么学>培训笔记_ch02_一些必须掌握的Linux命令. 本章的内容虽然多,基本都是书本原话,但是笔记能精 ...
- R语言学习基础一
笔者使用Rstudio编写R程序,本文主要总结在编写过程中遇到的一些实际 问题 与学习配套的的code上传到我的github,网址: https://github.com/LIU-HONGYANG/S ...
- SQL --------------- 运算符 = 与 in
in 用于指定查询与where 一块进行使用,可以用来指定一个或多个,和 “ = ” 差不多 语法: select * from 表名 where 字段 in (字段对应的值可以是一个或多个) 建个表 ...
- 还不错的PHP导出EXCEL函数挺好用的
直接上函数吧 //导出 $data内容二维数组 $title各个标题 $filename表名称 function exportexcelinfo($data=array(),$title=array( ...
- C#/.Net操作MongoDBHelper类
先 NuGet两个程序集 1:MongoDB.Driver. 2:MongoDB.Bson namespace ConsoleApp1{ /// <summary> /// Mongo ...