• Netty学习日记一:三大组件


    Channel & Buffer

    channel 有一点类似于 stream,它就是读写数据的双向通道,可以从 channel 将数据读入 buffer,也可以将 buffer 的数据写入 channel,而之前的 stream 要么是输入,要么是输出,channel 比 stream 更为底层

    channel
    buffer

    常见的 Channel

    • FileChannel
    • DatagramChannel
    • SocketChannel
    • ServerSocketChannel

    常见的 buffer

    buffer 则用来缓冲读写数据

    • ByteBuffer
      • MappedByteBuffer
      • DirectByteBuffer
      • HeapByteBuffer
    • ShortBuffer
    • IntBuffer
    • LongBuffer
    • FloatBuffer
    • DoubleBuffer
    • CharBuffer

    FileChannel的用法

    文件通道FileChannel是用于读取,写入,文件的通道。FileChannel只能被InputStream、OutputStream、RandomAccessFile创建。FileChannel通过字节流创建,那么操作的缓冲区肯定就是字节缓冲区。

    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.nio.ByteBuffer;
    import java.nio.channels.FileChannel;
    
    /**
     * @author: 随风飘的云
     * @describe:FileChannel的用法
     * @date 2022/08/04 22:48
     */
    public class FileChannelTest {
        // 使用通道读文件
        public void readData(File file){
            FileInputStream inputStream = null;
            try {
                inputStream = new FileInputStream(file);
                // 获取文件通道
                FileChannel channel = inputStream.getChannel();
                // 创建缓冲区
                ByteBuffer buffer = ByteBuffer.allocate(1024);
                // 从通道读取数据到缓冲区
                channel.read(buffer);
                // 反转缓冲区(limit设置为position,position设置为0,mark设置为-1)
                buffer.flip();
                // 就是判断position和limit之间是否有元素
                while (buffer.hasRemaining()){
                    // 按照字节的格式获取数据
                    System.out.print((char) buffer.get());
                }
                buffer.clear();
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                try {
                    if(inputStream != null){
                        inputStream.close();
                    }
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
    
        public void writeData(File file, String data){
            FileOutputStream outputStream = null;
            try {
                outputStream = new FileOutputStream(file);
                // 获取文件通道
                FileChannel channel = outputStream.getChannel();
                // 创建缓冲区
                ByteBuffer buffer = ByteBuffer.allocate(1024);
                // 将数据装入缓冲区
                buffer.put(data.getBytes());
                // 反转缓冲区(limit设置为position,position设置为0,mark设置为-1)
                buffer.flip();
                // 从通道读取数据到缓冲区
                channel.write(buffer);
                // 将缓冲区中的数据写入到通道
                channel.write(buffer);
                // 写完将缓冲区还原(position设置为0,limit设置为capacity,mark设置为-1)
                buffer.clear();
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                try {
                    if(outputStream != null){
                        outputStream.close();
                    }
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
    
        public static void main(String[] args) {
            FileChannelTest channelTest = new FileChannelTest();
            File file = new File("xxx.xxx  文件");
            channelTest.readData(file);
            channelTest.writeData(file, "1234567890");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82

    结果: 获取xxx文件的内容并且把1234567890写进该文件中。

    FileChannel的源码解析

    包位置:

    package java.nio.channels;
    
    • 1

    继承与实现关系

    public abstract class FileChannel  
        extends AbstractInterruptibleChannel  
        implements SeekableByteChannel, GatheringByteChannel, ScatteringByteChannel
    
    • 1
    • 2
    • 3

    构造器

      /** 
         * 初始化一个无参构造器. 
         */  
        protected FileChannel() { }
    
    • 1
    • 2
    • 3
    • 4

    具体方法:

    //打开或创建一个文件,返回一个文件通道来访问文件  
        public static FileChannel open(Path path,  
                                       Set<? extends OpenOption> options,  
                                       FileAttribute<?>... attrs)  
            throws IOException  
        {  
            FileSystemProvider provider = path.getFileSystem().provider();  
            return provider.newFileChannel(path, options, attrs);  
        }  
      
        private static final FileAttribute<?>[] NO_ATTRIBUTES = new FileAttribute[0];  
      
        //打开或创建一个文件,返回一个文件通道来访问文件  
        public static FileChannel open(Path path, OpenOption... options)  
            throws IOException  
        {  
            Set<OpenOption> set = new HashSet<OpenOption>(options.length);  
            Collections.addAll(set, options);  
            return open(path, set, NO_ATTRIBUTES);  
        }  
      
        //从这个通道读入一个字节序列到给定的缓冲区  
        public abstract int read(ByteBuffer dst) throws IOException;  
      
        //从这个通道读入指定开始位置和长度的字节序列到给定的缓冲区  
        public abstract long read(ByteBuffer[] dsts, int offset, int length)  
            throws IOException;  
      
        /** 
         * 从这个通道读入一个字节序列到给定的缓冲区 
         */  
        public final long read(ByteBuffer[] dsts) throws IOException {  
            return read(dsts, 0, dsts.length);  
        }  
      
        /** 
         * 从给定的缓冲区写入字节序列到这个通道 
         */  
        public abstract int write(ByteBuffer src) throws IOException;  
      
        /** 
         * 从给定缓冲区的子序列向该信道写入字节序列 
         */  
        public abstract long write(ByteBuffer[] srcs, int offset, int length)  
            throws IOException;  
      
        /** 
         * 从给定的缓冲区写入字节序列到这个通道 
         */  
        public final long write(ByteBuffer[] srcs) throws IOException {  
            return write(srcs, 0, srcs.length);  
        }  
      
        /** 
         * 返回通道读写缓冲区中的开始位置 
         */  
        public abstract long position() throws IOException;  
      
        /** 
         * 设置通道读写缓冲区中的开始位置 
         */  
        public abstract FileChannel position(long newPosition) throws IOException;  
      
        /** 
         * 返回此通道文件的当前大小 
         */  
        public abstract long size() throws IOException;  
      
        /** 
         * 通过指定的参数size来截取通道的大小 
         */  
        public abstract FileChannel truncate(long size) throws IOException;  
      
        /** 
         * 强制将通道中的更新文件写入到存储设备(磁盘等)中 
         */  
        public abstract void force(boolean metaData) throws IOException;  
      
        /** 
         * 将当前通道中的文件写入到可写字节通道中 
         * position就是开始写的位置,long就是写的长度 
         */  
        public abstract long transferTo(long position, long count,  
                                        WritableByteChannel target)  
            throws IOException;  
      
        /** 
         * 将当前通道中的文件写入可读字节通道中 
         * position就是开始写的位置,long就是写的长度 
         */  
        public abstract long transferFrom(ReadableByteChannel src,  
                                          long position, long count)  
            throws IOException;  
      
        /** 
         * 从通道中读取一系列字节到给定的缓冲区中 
         * 从指定的读取开始位置position处读取 
         */  
        public abstract int read(ByteBuffer dst, long position) throws IOException;  
      
        /** 
         * 从给定的缓冲区写入字节序列到这个通道 
         * 从指定的读取开始位置position处开始写 
         */  
        public abstract int write(ByteBuffer src, long position) throws IOException;  
      
      
        // -- Memory-mapped buffers --  
      
        /** 
         * 一个文件映射模式类型安全枚举 
         */  
        public static class MapMode {  
      
            //只读映射模型  
            public static final MapMode READ_ONLY  
                = new MapMode("READ_ONLY");  
      
            //读写映射模型  
            public static final MapMode READ_WRITE  
                = new MapMode("READ_WRITE");  
      
            /** 
             * 私有模式(复制在写)映射 
             */  
            public static final MapMode PRIVATE  
                = new MapMode("PRIVATE");  
      
            private final String name;  
      
            private MapMode(String name) {  
                this.name = name;  
            }  
        }  
      
        /** 
         * 将该通道文件的一个区域直接映射到内存中 
         */  
        public abstract MappedByteBuffer map(MapMode mode,  
                                             long position, long size)  
            throws IOException;  
      
        /** 
         * 获取当前通道文件的给定区域上的锁 
         * 区域就是从position处开始,size长度  
         * shared为true代表获取共享锁,false代表获取独占锁 
         */  
        public abstract FileLock lock(long position, long size, boolean shared)  
            throws IOException;  
      
        /** 
         * 获取当前通道文件上的独占锁 
         */  
        public final FileLock lock() throws IOException {  
            return lock(0L, Long.MAX_VALUE, false);  
        }  
      
        /** 
         * 尝试获取给定的通道文件区域上的锁 
         * 区域就是从position处开始,size长度  
         * shared为true代表获取共享锁,false代表获取独占锁 
         */  
        public abstract FileLock tryLock(long position, long size, boolean shared)  
            throws IOException;  
      
        /** 
         * 尝试获取当前通道文件上的独占锁 
         */  
        public final FileLock tryLock() throws IOException {  
            return tryLock(0L, Long.MAX_VALUE, false);  
        }  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171

    Selector

    Selector的作用是Java NIO中管理一组多路复用的SelectableChannel对象,并能够识别通道是否为诸如读写事件做好准备的组件selector。单从字面意思不好理解,需要结合服务器的设计演化来理解它的用途。

    多线程版设计

    多线程版
    socket1
    thread
    socket2
    thread
    socket3
    thread

    ⚠️多线程版缺点

    • 内存占用高
    • 线程上下文切换成本高
    • 只适合连接数少的场景

    线程池版设计

    线程池版
    socket1
    thread
    socket2
    thread
    socket3
    socket4

    ⚠️线程池版缺点

    • 阻塞模式下,线程仅能处理一个 socket 连接
    • 仅适合短连接场景

    selector 版设计

    selector 的作用就是配合一个线程来管理多个 channel,获取这些 channel 上发生的事件,这些 channel 工作在非阻塞模式下,不会让线程吊死在一个 channel 上。适合连接数特别多,但流量低的场景(low traffic)

    selector 版
    selector
    thread
    channel
    channel
    channel

    调用 selector 的 select() 会阻塞直到 channel 发生了读写就绪事件,这些事件发生,select 方法就会返回这些事件交给 thread 来处理

  • 相关阅读:
    数巅科技联合长江计算打造AskBot企业智能问答一体机
    Python 生成器 (通俗讲解)
    SVM 用于将数据分类为两分类或多分类(Matlab代码实现)
    大数据必学Java基础(九十六):PreparedStatement完成CURD和批处理
    软考系统架构师知识点集锦二:软件工程
    APS学习-LEKIN
    【WGS84坐标系】经纬度高度转换为WGS84坐标系对应的坐标值
    Mybatis日期检索格式报错
    95.(cesium之家)cesium动态单体化-3D建筑物(楼栋)
    Post-GWAS: LDSC 的 Partitioned Heritability 分析
  • 原文地址:https://blog.csdn.net/m0_46198325/article/details/126167986