美文网首页
通道(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());//啦啦哈哈吧吧
        }
    }
    

    相关文章

      网友评论

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

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