nio知识总结

Java nio的核心组成

Channel
Buffer
Selector

channel和buffer

概念: 在nio中,数据基本上都是从channel中开始,channel有些像流,数据即可以从channel读入到buffer中,同样也可以从buffer中写入到channel中
* java nio中channel的主要实现列表
1.FileChannel
2.DatagramChannel
3.socketChannel
4.ServerSocketChannel
这些类覆盖UDP和TCP网路IO,以及文件IO
* java nio中buffer的主要实现列表
1.ByteBuffer
2.CharBuffer
3.DoubleBuffer
4.FloatBuffer
5.IntBuffer
6.LongBuffer
7.ShortBuffer
8.MappedByteBuffer :内存映射文件

selector

selector允许单线程处理多个channel,要使用selector,你得先去向selector注册channel,然后调用它的select()方法,这个方法会一直阻塞到某个注册事件有事件就绪,一旦这个方法进行返回,那么线程就可以处理这些事件。

Buffer详解

Java Nio中buffer是用于与NIO通道进行交互的,数据从通道中读入到缓冲区,然后从缓冲区写入到通道中。缓冲区的本质就是一块能够写入数据,然后从中读取数据的内存。该块内存被封装成nio buffer对象,并提供了一组方法,用来方便的访问该块内存。

1.Buffer的基本用法 - 使用byteBuffer读写数据
(1)写数据到Buffer中 (相当于把数据写到内存中)
(2)调用flip()方法
(3)从Buffer中读取数据
(4)调用clear或者compact()方法
当向buffer写入数据时,buffer会记录下写了多少数据。一旦要读取数据,需要通过flip()方法将Buffer从写模式切换到读模式。在读模式下,可以读取之前写入到buffer的所有数据。一旦读完了所有的数据,就需要清空缓冲区,让它可以再次被写入。有两种方式能清空缓冲区:调用clear()或compact()方法
clear(): 清空整个缓冲区
compact(): 清空已经读过的数据,任何未读的数据都被移到缓冲区的起始处,新写入的数据将放到缓冲区未读数据的后面

2.Buffer实现原理
buffer中有3个重要的概念
*position指针
*capacity指针
*limit指针
capacity: 作为一个内存块,Buffer有一个固定的大小值,只能往里面写capacity个byte、long、char等类型,一旦Buffer满了,需要将其清空(通过读数据或者清除数据)才能继续写数据往里写数据。- 固定值
position:
(1)写数据状态
当你写数据到Buffer中时,position表示当前的位置,初始的position为0,当一个byte、long等数据写到Buffer后,position会向前移动到下一个可以插入数据的buffer单元,position最大值是 capacity – 1
(2)读数据状态
当读取数据时,也是从某个特定的位置开始读取,当将Buffer从写的模式切换到读的模式时,position会被重置成0,当从Buffer的position处读取数据时,position向前移动到下一个可读的位置。
limit:
(1)在写模式下
Buffer的limit表示你最多能往Buffer里写多少数据。写模式下,limit等于Buffer的capacity
(2)切换到读的模式下
limit表示最多能读取到多少的数据。因此,当buffer切换到读的模式时,limit会设置成写模式下的position值

3.clear和compact方法实现原理

clear方法: 把position指向0,它并不会去清除里面的数据,所以无法分辨出哪些数据已经读取过,哪些数据未读取
compact方法: 把position设置到未读数据的前面,方便下次读取的时候继续进行。compact()方法将所有未读的数据拷贝到Buffer起始处。然后将position设到最后一个未读元素正后面,limit依然设置成capacity的值

package com.learn.nio;

import org.junit.Test;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.ByteBuffer;

/**
 * Created by zhouxi on 2017/5/30 0030.
 *
 * @author  xzhou
 */
public class LearnBuffer {

    @Test
    public void testBufferPosition() {

        try {

            RandomAccessFile rfile = new RandomAccessFile("E:\\code\\learn\\nio\\testBuffer.txt", "rw");
            FileChannel channel = rfile.getChannel();
            ByteBuffer buffer = ByteBuffer.allocate(48);  // 定义48个字节的buffer空间
            int readByte = channel.read(buffer);          // 把数据写入到buffer中
            System.out.println("---------写的模式----------");
            System.out.println("readByte的值为:" + readByte);
            System.out.println("position:" + buffer.position());
            System.out.println("limit:" + buffer.limit());
            System.out.println("capacity:" + buffer.capacity());

            buffer.flip();  // 由写的模式转变成读的模式
            System.out.println("---------读的模式-----------");
            System.out.println("position:" + buffer.position());
            System.out.println("limit:" + buffer.limit());
            System.out.println("capacity:" + buffer.capacity());

            while(buffer.hasRemaining()) {
                System.out.print((char)buffer.get());
            }

            System.out.println("---------当读取值的时候-----------");
            System.out.println("position:" + buffer.position());
            System.out.println("limit:" + buffer.limit());
            System.out.println("capacity:" + buffer.capacity());

            buffer.rewind();

            System.out.println("--------rewind()模式下-----------");
            System.out.println("position:" + buffer.position());
            System.out.println("limit:" + buffer.limit());
            System.out.println("capacity:" + buffer.capacity());

            buffer.clear();  // clear方法,它并不会清除buffer中的数据

            System.out.println("--------clear()模式下-----------");
            System.out.println("position:" + buffer.position());
            System.out.println("limit:" + buffer.limit());
            System.out.println("capacity:" + buffer.capacity());

            System.out.println("获取第一个数: " + (char)buffer.get());
            System.out.println("position:" + buffer.position());

            buffer.put("123".getBytes());
            System.out.println("position:" + buffer.position());

            buffer.flip();

            while(buffer.hasRemaining()) {
                System.out.print((char)buffer.get());
            }

            buffer.clear();
            buffer.position(3);
            System.out.println("\n-----------清除其中的数据-----------");

            while(buffer.hasRemaining()) {
                System.out.print((char)buffer.get());
            }

            buffer.compact();

            System.out.println("---------capacity--------------------");
            System.out.println("position: " + buffer.position());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

scatter/gather(分散和聚集)

1.概念
(1)scatter(分散): 从Channel中读取是指在读的操作时,将读取的数据写入到多个buffer中,因此,Channel将从Channel读取的数据分散到多个buffer中。
(2)gather(聚集): 写入Channel是指在写的操作中,将多个buffer的数据同时写入到同一个Channel中,因为Channel将多个buffer的数据聚集再进行发送的使用场景。

package com.learn.nio;

import org.junit.Test;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

/**
 * Created by zhouxi on 2017/5/31 0031.
 *
 * @author xzhou
 */
public class LearnScatterGather {

    /**
     * scatter和gather的使用 - 分散和聚合
     */
    @Test
    public void testScatterGather() throws IOException {

        // scatter分散读取到buffer

        ByteBuffer header = ByteBuffer.allocate(48);
        ByteBuffer body = ByteBuffer.allocate(128);
        ByteBuffer[] bufferArray = {header, body};
        RandomAccessFile rfile = new RandomAccessFile("E:\\code\\learn\\nio\\testBuffer.txt", "rw");
        FileChannel fileChannel = rfile.getChannel();
        fileChannel.read(bufferArray);

        // gather聚集到channel中

        ByteBuffer head = ByteBuffer.allocate(128);
        ByteBuffer buBody = ByteBuffer.allocate(1024);
        ByteBuffer[] array = {head, buBody};
        fileChannel.write(buBody); // buffer向buffer数组写数据
    }
}

缓冲区分片和数据共享

buffer可以通过slice方法创建一个子缓冲区,即它创建了一个新的缓冲区,新缓冲区和原来的缓冲区共享一部分数据。接下来我们看下面代码例子,通过slice方法创建一个子缓冲区,然后修改子缓冲区的数据,看看原先的缓冲区数据会如何变化。

package com.learn.nio;

import org.junit.Test;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

/**
 * 缓冲区分片
 *
 * Created by zhouxi on 2017/6/1 0001.
 */
public class LearnSlice {

    @Test
    public void testSlice() {

        try {

            RandomAccessFile rfile = new RandomAccessFile("E:\\code\\learn\\nio\\testBuffer.txt", "rw");
            FileChannel channel = rfile.getChannel();
            ByteBuffer buffer = ByteBuffer.allocate(48);  // 定义48个字节的buffer空间
            channel.read(buffer);    // 把数据写入到channel中

            buffer.flip();

            while (buffer.hasRemaining()) {
                System.out.print((char)buffer.get());
            }

            System.out.println();

            // 数据分片的效果

            buffer.position(3);
            buffer.limit(8);
            ByteBuffer newBuffer = buffer.slice();   // newBuffer变成子缓冲区

            System.out.println("--------newBuffer开始分片数据列表-------");

            while(newBuffer.hasRemaining()) {
                System.out.print((char)newBuffer.get());
            }

            /*
             * 进行数据共享;子缓冲区共享数据
             *
             */
            newBuffer.flip();
            System.out.println("\nnewBuffer position:" + newBuffer.position());

            for(int i = 0; i < newBuffer.capacity(); i++) {
                byte b = newBuffer.get(i);
                b += 3;
                newBuffer.put(i, b);
            }

            while(newBuffer.hasRemaining()) {
                System.out.print((char)newBuffer.get());
            }

            buffer.flip();

            System.out.println();
            buffer.limit(21);
            System.out.println("buffer position:" + buffer.position());
            System.out.println("buffer limit:" + buffer.limit());
            System.out.println("buffer capacity:" + buffer.capacity());

            while(buffer.hasRemaining()) {
                System.out.print((char)buffer.get());
            }

        } catch(Exception e) {
            e.printStackTrace();
        }
    }
}

只读缓冲区

只读缓冲区非常简单 ― 你可以读取它们,但是不能向它们写入,可以通过调用缓冲区的asReadOnlyBuffer() 方法,将任何常规缓冲区转换为只读缓冲区,这个方法返回一个与原缓冲区完全相同的缓冲区(并与其共享数据),只不过它是只读的。
只读缓冲区对于保护数据很有用;在将缓冲区传递给某个对象的方法时,你无法知道这个方法是否会修改缓冲区中的数据。创建一个只读的缓冲区可以保证该缓冲区不会被修改。不能将只读的缓冲区转换为可写的缓冲区!

channel通道之间的传输

可以通过方法将数据直接从一个channel传输到另外一个channel

package com.learn.nio;

import org.junit.Test;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;

/**
 * Created by zhouxi on 2017/5/31 0031.
 *
 * @author  xzhou
 */
public class LearnTransferFrom {

    @Test
    public void testTransfer() {

        try {

            RandomAccessFile rfile = new RandomAccessFile("E:\\code\\learn\\nio\\testBuffer.txt","rw");
            FileChannel fromChannel = rfile.getChannel();
            RandomAccessFile afile = new RandomAccessFile("E:\\code\\learn\\nio\\transfer.txt","rw");
            FileChannel toChannel = afile.getChannel();

            long position = 0;
            long count = fromChannel.size();
            System.out.println("fromChannel size:....." + count);

            /*
             * transferFrom方法: 将数据从源通道传输到FileChannel
             *
             * position表示在toChannel在哪个位置进行开始插入数据
             */
            toChannel.transferFrom(fromChannel,2,10); // nio netty
            System.out.println("toChannel size:......" + toChannel.size());

            /*
             * transferTo方法: FileChannel传输到其他的channel中
             *
             * position是fromChannel中的从哪个位置截取数据进行插入
             *
             */

            System.out.println("toChannel的position的位置:" + toChannel.position());

            fromChannel.transferTo(0,5,toChannel);

            System.out.println("toChannel size: " + toChannel.size());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

 

转载于:https://www.cnblogs.com/persist-success/p/6993475.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值