JAVA基础之IO流
1.同步与异步,阻塞与非阻塞
●同步:方法1调用方法2,方法1必须等到方法2返回后,才能继续进行。
●异步:方法1调用方法2后,当方法2完成的时候会主动通知方法1
●阻塞:当前进程发送一个请求后,直接挂起,直到请求回复后才继续工作。
●非阻塞:发送请求后,不用一直等待结果,可以先去干别的事,每隔一段时间检查下是否返回。(和异步差不多嘛)
●同步阻塞:方法1调用方法2后,就一直等着方法2完成,完成后,再继续干别的
●异步阻塞:方法1调用方法2后,虽然方法2会主动告诉方法1何时完成了,但是由于是阻塞的,方法1还是一直在等着。
●同步非阻塞:方法1调用方法2后,去做别的了,过一段时间主动看一下方法2是否完成。
●异步非阻塞:方法1调用方法2后,去做别的了,方法2完成后会主动告诉方法1,我完成了。
2.BIO(Blocking I/O)
●同步阻塞I/O:线程发起IO请求后,一直阻塞,直到数据读/写完成后,再进行下一步操作。如果要同时处理多个请求,必须使用多线程,开销大。内核将数据准备好之前,系统调用会一直等待所有的套接字,默认是BIO
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yhkFd8ES-1650425898086)(http://luxiaolumm.gitee.io/luxiao-lu-mm/pic/109.png)]
3.NIO(NoBlockingI/O)
●同步非阻塞I/O模型:线程发起IO请求后,立即返回,去做别的事,但是需要定时过来检查一下数据是否读写完成。偶尔看一眼,如果有IO请求了,就进行处理,其他时间干别的。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DNVc0j01-1650425898087)(http://luxiaolumm.gitee.io/luxiao-lu-mm/pic/110.png)]
4.AIO(asynchronous I/O)
●异步非阻塞I/O模型:基于回调机制实现,线程发起IO请求后立即返回(非阻塞),当数据读写完成后,操作系统会通知线程进行后续操作(异步)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Cjbx2uCp-1650425898088)(http://luxiaolumm.gitee.io/luxiao-lu-mm/pic/111.png)]
5.IO流分类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nyo8YqcR-1650425898088)(http://luxiaolumm.gitee.io/luxiao-lu-mm/pic/112.png)]
●按处理数据单位分类
■字符流:以字符为单位,每次读入或读出16位的数据,只能读取字符类型数据(Java代码接受数据类型一般为char数组)
■字节流:以字节为单位,每次读入或读出8位数据。可以读任何类型的数据,图片,文字,视频(Java代码接受数据类型为byte数组),为什么各种文件都是以字节流读取呀,------这个看电脑文件属性即可,CPU规定计算机存储文件单位以字节来计算。
●按流的方向:
■输入流:从文件读入到内存,只能进行读操作
■输出流:从内存读出到文件,只能进行写操作,可以帮助我们创建文件。
●按是否直接与硬盘、内存等连接:
■节点流:直接与数据源相连,读入或读出
■处理流:对已存在的流进行包装,通过所封装的流的功能实现调用数据读写。如添加个Buffering缓冲区。
6.比特(Bit)、字节(Byte)、字符(char)
●Bit是最小的二进制单位,取值0/1,计算机只认识这个
●Byte计算机最小存储单位,是一个8位的二进制数(1Byte = 8Bit,取值-128到127)
●Char是用户可读的最小单位(Java里 char = 16bit = 2Byte)
7.JavaIO流
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9X1TZywT-1650425898089)(http://luxiaolumm.gitee.io/luxiao-lu-mm/pic/113.png)]
●抽象基类:InputStream、OutputStream(字节流)、Reader、Writer(字符流)所有流都是从这四个抽象基类派生出来的。
●FileReader(字符流)
■ int read();返回读入的第一个字符。如果到达文件末尾,返回-1(因为读的是整形,所以用强转(char)给转化过来)
■ close();流的关闭,所有流操作后都要关闭,因为物理连接,jvm无法自动关闭。
■ int read(char [] a);将文件中的内容写到了数组中(每次读入个数由数组长度决定最大值)。并且返回值为读入的字符的个数,当为-1表示读完了。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yqXTND5q-1650425898090)(http://luxiaolumm.gitee.io/luxiao-lu-mm/pic/114.png)]
●FileWriter(字符流)
■ 使用构造器FileWriter(file,false)/FileWriter(file)则覆盖掉原来内容
■ 使用FileWriter(file,true)则在原有文件基础上追加
■write(String str);写入字符串str
■write(char [],int x, int y);写入字符数组从x到y的数据。
●BufferedReader(字符流)
■BufferedReader(FileReader)
■readLine():单行读取,读取为空返回null,效果同reader,只是进行了封装,加入了缓冲功能,避免频繁读写硬盘。
●BufferedWriter(字符流)
■BufferedWriter(FileWriter);
■write(String str);增加了缓冲功能,避免频繁读写硬盘。
●FileInputStream(字节流)
■ int read():返回读入的第一个字符。如果到达文件末尾,返回-1(因为读的是整形,所以用强转(byte)转回来)
■ int read(byte [] a);将文件中的内容写到数组中(每次读入个数由数组长度决定最大值)。并且返回值为读入的字符的个数,当为-1时表示读完了。
●FileOutputStream(字节流)
■write(byte [],int x,int y);写入字符数组中从x到y的数据。
●BufferedInputStream(带缓冲区字节输入流)
■BufferedInputStream(InputStream)
■int read(byte[] a);将a文件中的内容写到数组中,(每次读入个数由数组长度决定最大值),并且返回值为读入的字节长度,-1表示读完了;数组越大交互次数越少,但是内存消耗巨大,一般为1024
●BufferedOutputStream(带缓冲区字节输出流):避免每次都和硬盘交互,提高访问效率
■BufferedOutputStream(OutputStream);
■write(byte[],int x,int y);
●注:关闭的时候先关外层的缓冲流,内部的流会自动关闭
●为什么提高效率:内部提供了一个缓存器,长度8192,超过这个长度,输入流内部提供方法flush自动刷新缓冲区。
●标准输入流:System.in(键盘,默认)是inputStream的类型
●标准输出流:System.out(控制台,默认)是PrintStream类型的(OutputStream的子类)
8.网络IO
●BIO编程的问题:BIO是阻塞的,如果多个客户端访问会出现堵塞的问题(第一个客户端等待请求的过程中,其他用户也要陪着等,->同步,第一个用户也没法干别的->阻塞),非常浪费资源。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mfZ2vL89-1650425898091)(http://luxiaolumm.gitee.io/luxiao-lu-mm/pic/115.png)]
●使用多线程解决BIO阻塞问题:使用多线程确实可以解决堵塞等待时间长的问题,因为其可以充分发挥CPU,但是系统资源是有限的啊,而且多个线程之间切换上下文,也会带来很大的性能消耗。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ARM0dbVf-1650425898092)(http://luxiaolumm.gitee.io/luxiao-lu-mm/pic/116.png)]
●使用线程池解决线程过多的问题:线程池固然可以解决这个问题,当需求量大的时候也可以扩充线程池,但是如果Socket上来就去创建线程抢占CPU资源,所有的线程都去IO了,CPU利用率也很低。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rNCiB4Kq-1650425898093)(http://luxiaolumm.gitee.io/luxiao-lu-mm/pic/117.png)]
●使用NIO实现网络通信(JDK官方答案,JDK1.4时出现):客户端的连接请求都会注册到多路复用器上,多路复用器轮询到连接有IO请求时候才启动一个线程进行处理。NIO方式适用于连接数目多且连接比较短的架构(redis就是这个)重要组件为:
■通道:也叫多路复用器,是一个对象,可以通过它读取或写入数据。通常都是将数据写入包含一个或多个字节的缓冲区,然后再将缓冲区的数据写入通道,将数据从通道读入缓冲区,再从缓冲区获取数据。类似于IO中的流,不过流是单向的,通道是双向的,可读可写。流是阻塞的,通道可以异步读写。
■选择器:每次客户端来了以后,就会把通道注册到选择器中,并给他一个状态。用死循环来循环判断(判断是否完成某个操作,完成某个操作后改为不一样的状态)状态是否发生了变化,变化了即IO完成,退出死循环。
■缓冲区:和Buffer原理相同,底层为一个数组,内部维护几个状态变量,可以在一块缓冲区上反复读写。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WqCUqUuF-1650425898094)(http://luxiaolumm.gitee.io/luxiao-lu-mm/pic/118.png)]