什么是IO
这里这个I指的是input,而这个O指的则是output,对于IO的认识就是输入输出。在讨论IO的时候,流则是关键中的关键
- 按数据流向分类
输入流和输出流 按处理的数据类型分类
字节流和字符流输入流和输出流
按照流的流向来分,可以分为输入流和输出流。输入,输出都是从程序运行所在内存的角度来划分的。
输入流:只能从中读取数据,而不能向其写入数据,由InputStream和Reader作为基类。
输出流:只能向其写入数据,而不能从中读取数据。由OutputStream和Writer作为基类- 字节流和字符流
字节流和字符流的用法几乎完全一样,区别在于字节流和字符流所操作的数据单元不同。
字节流操作的数据单元是8位字节,由InputStream和OutputStream作为基类。
字符流操作的数据单元是16位的字符,由Reader和Writer作为基类(提供了转换流) 节点流和处理流
按照流的角色来分,可以分为节点流和处理流。
节点流:可以从向一个特定的IO设备(如磁盘、网络)读/写数据的流。也被称为低级流。
处理流:用于对一个已存在的流进行连接或封装,通过封装后的流来实现数据读/写功能。也称为高级流AIO/NIO/BIO
AIO (NIO2)是彻底的异步非阻塞。NIO (I/O多路复用)是同步非阻塞通信。BIO 是同步阻塞。
假设有这么一个场景,有一排水壶(客户)在烧水。同步说的是线程同步,阻塞说的是水壶阻塞。
AIO的做法是,每个水壶上装一个开关,当水开了以后会提醒对应的线程去处理。
NIO的做法是,叫一个线程不停的循环观察每一个水壶,根据每个水壶当前的状态去处理。
BIO的做法是,叫一个线程停留在一个水壶那,直到这个水壶烧开,才去处理下一个水壶。
BIO、NIO、AIO适用场景分析:BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解。
- NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持。
AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。
NIO 同步非阻塞
NIO将多个IO的阻塞复用到同一个select的阻塞上,从而使得系统在单线程的情况下处理多个客户端请求。
NIO三个核心对象:通道(Channel)、缓冲区(Buffer)和选择器(Selector)
具体说就是Selector会不断轮询注册在其上的Channel,如果某个Channel上有新的TCP连接,读或者写事件,这个Channel就处于就绪状态,会被Selector轮询出来,然后通过SelectorKey可以获取就绪Channel的集合,进行后续I/O操作。
select,poll,epoll都是IO多路复用的机制。I/O多路复用就是通过一种机制,一个进程可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间。select
在发生网络IO请求的时候,select只能仅仅知道发生了IO请求,但是它不知道是哪个网络连接发生的,所以它只能通过遍历所有的流,找到发生IO事件的流,然后进行后续的操作。所以处理的流越多,遍历的时间越长。
select本质上是通过设置或者检查存放fd标志位的数据结构来进行下一步处理。这样所带来的缺点是:单个进程所打开的FD是有限制的,通过 FD_SETSIZE 设置,默认1024 ;
poll
poll本质上和select没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态, 但是它没有最大连接数的限制,原因是它是基于链表来存储的。
它没有最大连接数的限制,原因是它是基于链表来存储的,但是同样有缺点:每次调用 poll ,都需要把 fd 集合从用户态拷贝到内核态,这个开销在 fd 很多时会很大;
对 socket 扫描是线性扫描,采用轮询的方法,效率较低(高并发时)
epoll
epoll 可以理解为 event poll,不同于忙轮询和无差别轮询,epoll 会把哪个流发生了怎样的I/O事件通知我们。所以我们说epoll实际上是事件驱动(每个事件关联上fd)的,此时我们对这些流的操作都是有意义的。(复杂度降低到了O(1))
序列化
序列化的原本意图是希望对一个Java对象作一下“变换”,变成字节序列,这样一来方便持久化存储到磁盘,避免程序运行结束后对象就从内存里消失,另外变换成字节序列也更便于网络运输和传播,所以概念上很好理解:
序列化:把Java对象转换为字节序列。
-
什么时候进行序列化
当你想把内存中的对象写入到硬盘时
- 当你想用套接字在网络上传输对象时
当你想通过RMI调用对象时(RMI是什么东西?):RMI总结来说就是远程调用对象,在一个jvm上调用另一个jvm的对象。
序列化需要注意的事项
序列化只保存对象的状态,而不管对象的方法。被
static
修饰的字段是不会被序列化的- 凡是被
transient
修饰符修饰的字段也是不会被序列化的 - 当一个父类实现了序列化,它的子类也自动实现序列化,不用显示进行实现了。
当一个实例对象引用其他对象,当序列化该对象时也把引用的对象进行了实例化,利用对象序列化可以进行对象的”深复制”。
String、StringBuffer、StringBuilder
String的值是不可变的,这就导致每次对String的操作都会生成新的String对象
如果要操作少量的数据用 String;
多线程操作字符串缓冲区下操作大量数据 StringBuffer;
单线程操作字符串缓冲区下操作大量数据 StringBuilder。参考资料
- Java 转换流(字节流与字符流之间的转换)
- NIO与IO的区别
- Java面试总结之AIO与NIO
- Java序列化有什么作用?序列化与不序列化有什么区别
- 为什么我不建议你使用Java序列化
https://zhuanlan.zhihu.com/p/272891398
https://blog.csdn.net/weixin_43207025/article/details/110798935
https://blog.csdn.net/m0_45861545/article/details/122469405
https://blog.csdn.net/m0_69650487/article/details/124744973
https://www.likecs.com/show-203480096.html
https://www.csdn.net/tags/MtTakg0sOTQwNTMtYmxvZwO0O0OO0O0O.html