NIO

与IO的区别

IO: 

     面向流的(每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方。此外,它不能前后移动流中的数据。如果需要前后移动从流中读取的数据,需要先将它缓存到一个缓冲区。)

     IO的各种流是阻塞的(当一个线程调用read() 或 write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了)

     基于字节流和字符流

NIO:

    面向缓冲区的(数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动)

    非租塞(使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取。而不是保持线程阻塞,所以直至数据变得可以读取之前,该线程可以继续做其他的事情。)

    NIO基于Channel和Buffer(缓冲区)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中

NIO核心组成:

channel 通道:

    只不过Stream是单向的,譬如:InputStream, OutputStream.而Channel是双向的,既可以用来进行读操作,又可以用来进行写操作。
NIO中的Channel的主要实现有:

  • FileChannel

  • DatagramChannel

  • SocketChannel

  • ServerSocketChannel

这里看名字就可以猜出个所以然来:分别可以对应文件IO、UDP和TCP(Server和Client)

buffer 缓冲区:

      NIO中的关键Buffer实现有:ByteBuffer, CharBuffer, DoubleBuffer, FloatBuffer, IntBuffer, LongBuffer, ShortBuffer,分别对应基本数据类型: byte, char, double, float, int, long, short。当然NIO中还有MappedByteBuffer, HeapByteBuffer, DirectByteBuffer等这里先不进行陈述

selector:

    Selector运行单线程处理多个Channel,如果你的应用打开了多个通道,但每个连接的流量都很低,使用Selector就会很方便。例如在一个聊天服务器中。要使用Selector, 得向Selector注册Channel,然后调用它的select()方法。这个方法会一直阻塞到某个注册的通道有事件就绪。一旦这个方法返回,线程就可以处理这些事件,事件的例子有如新的连接进来、数据接收等。

使用Buffer:

buffer就像一个数组,可以保存多个类型相同的数据

public class NIOTest1 {
    public static void main(String[] args) {
        //创建Buffer
        CharBuffer buff = CharBuffer.allocate(8);
        System.out.println("capacity="+buff.capacity());
        System.out.println("limit="+buff.limit());
        System.out.println("position="+buff.position());
        //放入元素
        buff.put('a');
        buff.put('b');
        buff.put('c');
        System.out.println("放入a、b、c后");
        System.out.println("position="+buff.position());//
        buff.flip();//将limit设置为position所在的位置,并将position设为0
        System.out.println("flip后");
        System.out.println("limit="+buff.limit());
        System.out.println("position="+buff.position());
        System.out.println(buff.get());//取出第一个元素 ,相对读取--读取后将position加一
        System.out.println("buff.get()后");
        System.out.println("position="+buff.position());//取出第一个元素后的position
        System.out.println("clear()后");
        buff.clear();//将position设为0,将limit设置为capacity
        System.out.println("limit="+buff.limit());
        System.out.println("position="+buff.position());
        //执行clear()后,缓冲区内容并没有被清除,取出第三个元素
        System.out.println(buff.get(2));
        System.out.println("position="+buff.position());//执行绝对读取后,position扔不变
    }
}

使用Channel

Channel类似传统的流对象,但是和传统的流对象有区别:

Channel可以直接将指定文件的部分或全部直接映射成Buff;

程序不能直接访问Channel的数据,包括读取、写入都不行,Channel只能与Buffer进行交互

所有的Channel都不应该通过构造器来直接创建,而是通过传统的节点InputStream,OutputStream的getChanne()来返回对应的Channel ;

举例:利用通道完成文件的复制,非直接缓冲区

public class NIOTest2 {
    public static void main(String[] args) {
        File file = new File("E:/nio/a.txt");
        File file2 = new File("E:/nio/b.txt");
        byte[] bs = new byte[2];
        try{
            InputStream fis = new FileInputStream(file);
            OutputStream fos = new FileOutputStream(file2);
            //获取通道
            FileChannel inChannel = ((FileInputStream) fis).getChannel();
            FileChannel outChannel = ((FileOutputStream) fos).getChannel();
            //分配指定大小缓存区
            ByteBuffer buff = ByteBuffer.allocate(1024);// position 0 ,limit 1024
            //将通道的数据存入缓存区
            while(inChannel.read(buff)!=-1){// position 1024 ,limit 1024 ,相当于put
                //切换读模式
                buff.flip();//position 0 ,limit 1024 因为之前已经操作了buff,所以position
                //将缓存去的数据写入通道
                outChannel.write(buff);//position 1024 ,limit 1024,相当于get
                //清空缓冲区
                buff.clear();//position 0 ,limit 1024
            }
            outChannel.close();
            inChannel.close();
            fis.close();
            fos.close();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
https://blog.csdn.net/forezp/article/details/88414741?ops_request_misc=%25257B%252522request%25255Fid%252522%25253A%252522161280019116780264097838%252522%25252C%252522scm%252522%25253A%25252220140713.130102334..%252522%25257D&request_id=161280019116780264097838&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~baidu_landing_v2~default-1-88414741.first_rank_v2_pc_rank_v29&utm_term=java%252Cnio

索引

说明

capacity

缓冲区数组的总长度

position

下一个要操作的数据元素的位置

limit

缓冲区数组中不可操作的下一个元素的位置:limit<=capacity

mark

用于记录当前position的前一个位置或者默认是-1

640


无图无真相,举例:我们通过ByteBuffer.allocate(11)方法创建了一个11个byte的数组的缓冲区,初始状态如上图,position的位置为0,capacity和limit默认都是数组长度。当我们写入5个字节时,变化如下图:
640

这时我们需要将缓冲区中的5个字节数据写入Channel的通信信道,所以我们调用ByteBuffer.flip()方法,变化如下图所示(position设回0,并将limit设成之前的position的值):

640

这时底层操作系统就可以从缓冲区中正确读取这个5个字节数据并发送出去了。在下一次写数据之前我们再调用clear()方法,缓冲区的索引位置又回到了初始位置。

调用clear()方法:position将被设回0,limit设置成capacity,换句话说,Buffer被清空了,其实Buffer中的数据并未被清除,只是这些标记告诉我们可以从哪里开始往Buffer里写数据。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值