美文网首页
通道(Channel)与缓冲区(Buffer)

通道(Channel)与缓冲区(Buffer)

作者: 笨比乔治 | 来源:发表于2020-12-12 10:09 被阅读0次

Selector会不断地轮询注册在其上的Channel,如果某个Channel上面有新的TCP连接接入、读和写事件,这个Channel就处于就绪状态,会被Selector轮询出来,然后通过SelectionKey可以获取就绪Channel的Set集合,进行后续的I/O操作。

Java NIO (New IO Non Blocking IO)是从Java1.4版本开始引入的一个新的IO API ,可以替代标准的Java IO API。NIO与原来的IO有同样的作用和目的,但是使用的方式完全不同,NIO支持面向缓冲区、基于通道的IO操作。NIO将以更加高效的方式进行文件的读写操作。

IMG_2197(20201121-101659).JPG IMG_2198(20201121-103358).JPG IMG_2199(20201121-103413).JPG IMG_2200(20201121-103533).JPG

1、缓冲区(Buffer)

缓冲区(Buffer) :一个用于特定基本数据类型的容器。由 java.nio 包定义的,所有缓冲区都是 Buffer 抽象类的子类。
Java NIO 中的 Buffer 主要用于与 NIO 通道进行交互,数据是从通道读入缓冲区,从缓冲区写入通道中的。

这里写图片描述
/**
 * @author qiz
 */
public class NIO {
    public static void main(String[] args) {
        //分配一个指定大小的缓冲区
        //缓冲区的管理方式几乎一致,通过allocate()获取缓冲区
        ByteBuffer allocate = ByteBuffer.allocate(1024);
        //缓冲区存区数据的两大核心方法
        //put() 存入数据  get()获取缓冲区数据
        /**
         *  private int mark = -1;
         *     private int position = 0; 位置,表示缓冲区中正在操作数据的位置
         *     private int limit; 限制/界限 缓冲区中可以操作数据的大小 limit后面的数据不能改变
         *     private int capacity;容量,表示缓冲区最大存储数据的容量,一旦声明不能改变。
         *
         */
        System.out.println(allocate.limit());
        System.out.println(allocate.position());
        System.out.println(allocate.capacity());
    }
}

Buffer 的常用方法

这里写图片描述
非直接缓冲区
这里写图片描述

直接缓冲区

这里写图片描述 image.png image.png image.png

DMA(Direct Memory Access,直接存储器访问)。在DMA出现之前,CPU与外设之间的数据传送方式有程序传送方式、中断传送方式。CPU是通过系统总线与其他部件连接并进行数据传输。
DMA的出现就是为了解决批量数据的输入/输出问题。DMA是指外部设备不通过CPU而直接与系统内存交换数据的接口技术。这样数据的传送速度就取决于存储器和外设的工作速度。
在DMA方式中,数据的传送方向、存放数据的内存始址以及传送的数据块长度等都由CPU控制,而在通道方式中,这些都由通道来进行控制。另外,DMA方式每台设备至少需要一个DMA控制器,一个通道控制器可以控制多台设备。
DMA是为了解放在IO操作时每一个字符都需要由CPU来管理传输, 在接到网卡中断时, cpu会初始化DMA, 打个比方, 你在打游戏,
可是门铃响了, 外卖到了, 你new 了一个girl friend 告诉她, 你定的什么外卖, 数量多少, 需要放在那里, 然后你继续好好打游戏, 其他交由她去完成(去的快递, 放入指定位置, 校验是否完成), done之后, 会回来中断你打游戏, 你就可以去吃外卖了, 节省你的精力
明白了DMA就好理解NIO的Channel了吧.
再来张图:

image

通道(Channel)用于源节点与目标节点的连接。在Java NIO中负责缓冲区数据的传输。Channel本身不存储数据,因此需要配合缓冲区进行传输。

image.png
/*
 * 一、通道(Channel):用于源节点与目标节点的连接。在java NIO中负责缓冲区中数据的传输。Channel本身不存储数据,需要配合缓冲区进行传输。
 * 
 * 二、通道的主要实现类
 *    java.nio.channels.Channel 接口:
 *        |--FileChannel:用于读取、写入、映射和操作文件的通道。
 *        |--SocketChannel:通过 TCP 读写网络中的数据。
 *        |--ServerSocketChannel:可以监听新进来的 TCP 连接,对每一个新进来的连接都会创建一个 SocketChannel。
 *        |--DatagramChannel:通过 UDP 读写网络中的数据通道。
 *        
 * 三、获取通道
 * 1.java针对支持通道的类提供了getChannel()方法
 *      本地IO:
 *      FileInputStream/FileOutputStream
 *      RandomAccessFile
 *      
 *      网络IO:
 *      Socket
 *      ServerSocket
 *      DatagramSocket
 *      
 * 2.在JDK 1.7 中的NIO.2 针对各个通道提供了静态方法 open()
 * 3.在JDK 1.7 中的NIO.2 的Files工具类的newByteChannel()
 * 
 * 四、通道之间的数据传输
 * transferFrom()
 * transferTo()
 * 
 * 五、分散(Scatter)与聚集(Gather)
 * 分散读取(Scattering Reads):将通道中的数据分散到多个缓冲区中
 * 聚集写入(Gathering Writes):将多个缓冲区中的数据聚集到通道中
 * 
 * 六、字符集:Charset
 * 编码:字符串-》字符数组
 * 解码:字符数组-》字符串
 */
public class TestChannel {

    //利用通道完成文件的复制(非直接缓冲区)
    @Test
    public void test1(){
        long start=System.currentTimeMillis();

        FileInputStream fis=null;
        FileOutputStream fos=null;

        FileChannel inChannel=null;
        FileChannel outChannel=null;
        try{
            fis=new FileInputStream("d:/1.avi");
            fos=new FileOutputStream("d:/2.avi");

            //1.获取通道
            inChannel=fis.getChannel();
            outChannel=fos.getChannel();

            //2.分配指定大小的缓冲区
            ByteBuffer buf=ByteBuffer.allocate(1024);

            //3.将通道中的数据存入缓冲区中
            while(inChannel.read(buf)!=-1){
                buf.flip();//切换读取数据的模式
                //4.将缓冲区中的数据写入通道中
                outChannel.write(buf);
                buf.clear();//清空缓冲区
            }
        }catch(IOException e){
            e.printStackTrace();
        }finally{
            if(outChannel!=null){
                try {
                    outChannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(inChannel!=null){
                try {
                    inChannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(fos!=null){
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(fis!=null){
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        long end=System.currentTimeMillis();
        System.out.println("耗费时间:"+(end-start));//耗费时间:1094
    }
    //使用直接缓冲区完成文件的复制(内存映射文件)
    @Test
    public void test2() {
        long start=System.currentTimeMillis();

        FileChannel inChannel=null;
        FileChannel outChannel=null;
        try {
            inChannel = FileChannel.open(Paths.get("d:/1.avi"), StandardOpenOption.READ);
            outChannel=FileChannel.open(Paths.get("d:/2.avi"), StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE);

            //内存映射文件
            MappedByteBuffer inMappedBuf=inChannel.map(MapMode.READ_ONLY, 0, inChannel.size());
            MappedByteBuffer outMappedBuf=outChannel.map(MapMode.READ_WRITE, 0, inChannel.size());
            //直接对缓冲区进行数据的读写操作
            byte[] dst=new byte[inMappedBuf.limit()];
            inMappedBuf.get(dst);
            outMappedBuf.put(dst);
        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            if(outChannel!=null){
                try {
                    outChannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(inChannel!=null){
                try {
                    inChannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        long end=System.currentTimeMillis();
        System.out.println("耗费的时间为:"+(end-start));//耗费的时间为:200
    }
    //通道之间的数据传输(直接缓冲区)
    @Test
    public void test3(){
        long start=System.currentTimeMillis();

        FileChannel inChannel=null;
        FileChannel outChannel=null;
        try {
            inChannel = FileChannel.open(Paths.get("d:/1.avi"), StandardOpenOption.READ);
            outChannel=FileChannel.open(Paths.get("d:/2.avi"), StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE);

            inChannel.transferTo(0, inChannel.size(), outChannel);
            outChannel.transferFrom(inChannel, 0, inChannel.size());
        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            if(outChannel!=null){
                try {
                    outChannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(inChannel!=null){
                try {
                    inChannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        long end=System.currentTimeMillis();
        System.out.println("耗费的时间为:"+(end-start));//耗费的时间为:147
    }
image.png
    //分散和聚集
    @Test
    public void test4(){
        RandomAccessFile raf1=null;
        FileChannel channel1=null;
        RandomAccessFile raf2=null;
        FileChannel channel2=null;
        try {
            raf1=new RandomAccessFile("1.txt","rw");

            //1.获取通道
            channel1=raf1.getChannel();

            //2.分配指定大小的缓冲区
            ByteBuffer buf1=ByteBuffer.allocate(100);
            ByteBuffer buf2=ByteBuffer.allocate(1024);

            //3.分散读取
            ByteBuffer[] bufs={buf1,buf2};
            channel1.read(bufs);

            for(ByteBuffer byteBuffer : bufs){
                byteBuffer.flip();
            }
            System.out.println(new String(bufs[0].array(),0,bufs[0].limit()));
            System.out.println("--------------------");
            System.out.println(new String(bufs[1].array(),0,bufs[1].limit()));

            //4.聚集写入
            raf2=new RandomAccessFile("2.txt", "rw");
            channel2=raf2.getChannel();

            channel2.write(bufs);

        }catch (IOException e) {
            e.printStackTrace();
        }finally{
            if(channel2!=null){
                try {
                    channel2.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(channel1!=null){
                try {
                    channel1.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(raf2!=null){
                try {
                    raf2.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(raf1!=null){
                try {
                    raf1.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    //输出支持的字符集
    @Test
    public void test5(){
        Map<String,Charset> map=Charset.availableCharsets();
        Set<Entry<String,Charset>> set=map.entrySet();

        for(Entry<String,Charset> entry:set){
            System.out.println(entry.getKey()+"="+entry.getValue());
        }
    }

    //字符集
    @Test
    public void test6(){
        Charset cs1=Charset.forName("GBK");

        //获取编码器
        CharsetEncoder ce=cs1.newEncoder();

        //获取解码器
        CharsetDecoder cd=cs1.newDecoder();

        CharBuffer cBuf=CharBuffer.allocate(1024);
        cBuf.put("啦啦哈哈吧吧");
        cBuf.flip();

        //编码
        ByteBuffer bBuf=null;
        try {
            bBuf = ce.encode(cBuf);
        } catch (CharacterCodingException e) {
            e.printStackTrace();
        }

        for(int i=0;i<12;i++){
            System.out.println(bBuf.get());//-64-78-64-78-71-2-7-2-80-55-80-55
        }

        //解码
        bBuf.flip();
        CharBuffer cBuf2=null;
        try {
            cBuf2 = cd.decode(bBuf);
        } catch (CharacterCodingException e) {
            e.printStackTrace();
        }
        System.out.println(cBuf2.toString());//啦啦哈哈吧吧
    }
}

相关文章

  • Java NIO(2) - 缓冲区(Buffer)

    3. 缓冲区(Buffer)和通道(Channel) Java NIO系统的核心在于:通道(Channel)和缓冲...

  • Java中的NIO

    NIO的几个概念 Channel(通道),Buffer(缓冲区),Selector(选择器) 1. 通道 顾名思义...

  • OOM之Direct buffer memory

    写NIO程序经常使用ByteBuffer来读取或写入数据,这是一种基于通道(Channel)与缓冲区(Buffer...

  • 学习笔记——NIO(2)关于Buffer

    Buffer是缓存,Buffer的作用是和NIO通道(Channel)进行交互。查阅资料知道: 缓冲区本质上是一块...

  • Java NIO与IO详解

    概述 NIO主要有三大核心部分:Channel(通道),Buffer(缓冲区),Selector。 传统IO基于...

  • NIO

    简述 三大核心组件:通道(channel),缓冲区(buffer),选择器(selector) 传统IO针对字节流...

  • 二.NIO简介

    一.概述 NIO主要有三大核心部分:Channel(通道),Buffer(缓冲区), Selector。 所有的 ...

  • NIO

    1 NIO主要有三大核心部分:Channel(通道),Buffer(缓冲区), Selector 2 Select...

  • Java高性能:干货分享——让你快速掌握《NIO》

    一、概述 NIO主要有三大核心部分:Channel(通道)、Buffer(缓冲区)、 Selector。 NIO和...

  • Java 中的nio

    概述 NIO主要有三大核心部分:Channel(通道),Buffer(缓冲区), Selector。 传统IO基于...

网友评论

      本文标题:通道(Channel)与缓冲区(Buffer)

      本文链接:https://www.haomeiwen.com/subject/upmsiktx.html