io与Nio知识梳理

##io体系架构回顾

一、io的输入流和输出流是两种,不能同时是输入流和输出流。

二、流的分类:1、节点流:从特定的地方读写的流类,和目标或目的地直接接触交互的,例如:磁盘或一块内存区域

2、过滤流:使用节点流作为输出或输出,在节点流基础上的包装。过滤流是使用一个已经存在的输入或输出流连接创建的

I/O流的连接

input:文件-》从文件中取输入字节(FileInputStream)-》增加缓存(BufferedInputStream)-》增加读取java基本数据类型的功能(DataInputStream)(用户使用)-》数据

output:数据-》往输出流中写入java基本数据类型(DataInputStream)-》提供数据写入到缓冲区的功能(BufferedInputStream)-》将数据写入文件(FileInputStream)-》文件

节点流:类A  过滤流:B、C    ->   new C(new B(new A())) ;(Decorator模式)

java的I/O库提供了一个称作连接的机制,可以将一个流与另一个流首尾相接,形成一个流管道连接。Decorator(装饰)模式。通过流的连接,可以动态增加流的功能,而这种功能的增加是通过组合一些流的基本功能而动态获取的。
I/O体系由很多类,通过装饰模式,不影响基础功能上,控制类的数量。

volatile 作用:1、内存可见性,A线程修改了自己工作内存的变量值,会修改主内存里变量的值,并刷新到B线程工作内存的值
2、防止指令重排序,编译后的字节码和编程语言的顺序不一致,加上volatile能防止。

##Nio体系

源码 https://github.com/chris1132/netty_lecture/src/main/java/com/chovy/nio

1、java.io最核心的概念是流(stream),IO编程就是面向流的编程,java中,一个流要么是输出流,要么是输出流。

2、java.nio中拥有三个核心概念:Selector,Channel和Buffer。在java.nio中,是面向块(block)或是缓冲区(buffer)编程的。

3、Buffer本身就是一块内存,底层实现上,实际上是个数组。数据的读、写都是通过Buffer来实现的。

4、io编程中,数据是从stream中直接读取到程序里,nio中,数据从channel读到buffer中,从buffer数组里读到程序。

nio中,buffer既可以写入数据,也可以从buffer读取,但当进行读、写切换时,需要调用buffer.flip()方法来反转。

5、除了数组之外,Buffer还提供了对于数组的结构化访问方式,并且可以追踪到系统读写过程

---》读和写在底层通过相应标识来判断读、写到什么位置,这些在buffer底层都封装好,无论读、写系统都能定位到当前读写的位置,以及flip反转后从哪读,读到什么地方。

6、java中的7种原生数据类型(byte、short、int、long、float、double、char)都有各自对应的buffer类型,如IntBuffer,LongBuffer等。boolean数据类型,没有buffer。

7、channel值得是可以向其写入数据或是从中读取数据的对象,类似于java.io中的stream,所有数据的读写都是通过buffer进行

与stream不同的是,channel是双向的,一个流只能是inputstream或是outputstream,channel打开后,则可以读取、写入、读写

由于channel是双向的,因此它能更好地反映出底层操作系统的真是情况,如在linux中,底层操作系统的通道是双向的,

8、关于NIO Buffer中的几个属性

capacity:buffer包含的元素的个数,分配好后永远不会变化,如:IntBuffer b = IntBuffer.allocate(10),元素的数量就是10。

limit:第一个不能再读或不能再写的元素的索引,值小于等于capacity。

position:下一个将要被读或写的元素的索引,值小于等于limit。

如下图,调用flip后,limit值为position的值,position的值为0。之后limit的值在每次flip后都为position的值

mark,reset:如position读取到5是,使用mark,进行标记,然后继续往下读,然后可以通过调用reset,重新从5开始读。0<=mark<=position<=limit<=capacity;

clear:将limit设为capacity,position设为0,将buffer设为初始化状态;

flip:将limit设为当前position,将position设为0,重头开始读写;

rewind:limit不变,position设为0,buffer重新读取。

绝对方法和相对方法

相对方法:limit和position的值,在操作时需考虑到,由netty底层方法进行更改,如flip方法。

绝对方法:limit和position的值不会更改,根据buffer的索引直接get和put。

9、ByteBuffer buffer = ByteBuffer.allocate(10); ByteBuffer sliceBuffer = buffer.slice();

slice方法是创建一个和当前buffer共享内容块的新buffer,sliceBuffer更改内容buffer里也能看到,因此,新的buffer其实是索引

10、HeapByteBuffer,操作系统不是直接处理java堆上的HeapByteBuffer里面的字节数组,而是拷贝一份到java堆外的native堆的内存空间,堆外空间的数据和IO设备交互。

为什么HeapByteBuffer不直接操作java堆上的数据呢,因为如果操作堆内存时,发生GC,GC把部分内存清理,并压缩内存以腾出更大的内存空间,这时如果是在读取内存数据,就会发生错乱,OutOfMemoryError。所以,先拷贝到native,再取。

DirectByteBuffer,java堆上不会存在字节数组,数组在native堆内存里,IO设备直接和native堆内存做交互。(ZeroCopy 零拷贝),当DirectByteBuffer被回收掉,堆外内存被回收。netty使用零拷贝,保证了它的高性能。

MappedByteBuffer,内存映射文件,直接从内存访问操作文件。在堆外内存。

11、scattering,gathering。

scattering(分散成多个)在读数据时可以传递buffer数组,将来自一个channe中的数据读到多个buffer中,是按照顺序读。如channel中有20个字节,然后三个buffer长度分别是(10,5,5),在读的时候先读满第一个,接着读满第二个,再第三个。

应用场景:如http的请求,header中的数据写入若干buffer,body中的数据写入另外的buffer,这样就自然的实现数据分

ByteBuffer header = ByteBuffer.allocate(128);
ByteBuffer body   = ByteBuffer.allocate(1024);
ByteBuffer[] bufferArray = { header, body }
channel.read(bufferArray);

gathering(合成一个),多个buffer数据写入到一个channel中,往外写的是时候可以传递一个buffer数组,在写的时候,先写第一个,在第二个.....

12、selector

https://github.com/chris1132/netty_lecture/blob/master/src/main/java/com/chovy/nio/NioTest11.java

需求:实现两个客户端A B 连接服务器,A向服务器发消息后,B收到A发的消息,打印消息来源客户端+消息内容。

nio server端编写:https://github.com/chris1132/netty_lecture/blob/master/src/main/java/com/chovy/nio/NioServer.java

ps:Set<SelectionKey>在用完后,要调用clear方法进行清理。

nio client端编写:https://github.com/chris1132/netty_lecture/blob/master/src/main/java/com/chovy/nio/NioClient.java

2018-8-24

字符集编解码

https://github.com/chris1132/netty_lecture/blob/master/src/main/java/com/chovy/nio/NioTestDecodeEncode.java

需求:文件拷贝

思考问题:当前使用utf-8,若改成iso-8859-1为什么不会出现乱码

首先,iso-8859-1是不会丢弃字节。对于字符“你”,utf编码用“AB BB CC”(举例)三个字节表示,虽然经过中间的编解码

        CharBuffer charBuffer = charsetDecoder.decode(inputData);
        ByteBuffer outputData = charsetEncoder.encode(charBuffer);

,但编+解码后,实际只是过渡,从整体看,是把源文件NioTest_in(utf-8)里的这些字节信息,输出到目标文件NioTest_out(utf-8),所以只要源文件和目标文件的字节编码一致,这里都是utf-8,因此不会出现乱码

回顾字符集编码历史:https://blog.csdn.net/u010530712/article/details/82020901

猜你喜欢

转载自blog.csdn.net/u010530712/article/details/82021416
今日推荐