当前位置: 首页 > news >正文

JAVA_NIO系列——Channel和Buffer详解

Java NIO是一个用来替代标准Java IO API的新型数据传递方式,像现在分布式架构中会经常存在他的身影。其比传统的IO更加高效,非阻塞,异步,双向

NIO主体结构

这里写图片描述

Java NIO的主要构成核心就是Buffer、Channel和Selector这三个

对于Channel我想要提醒的是,Channel中的数据总是要先读到一个Buffer,或者总是要从一个Buffer中写入

使用Selector,得向Selector注册Channel,然后调用它的select()方法。这个方法会一直阻塞到某个注册的通道有事件就绪。一旦这个方法返回,线程就可以处理这些事件

Channel

所有的 IO 在NIO 中都从一个Channel 开始。Channel 有点象流

Channel的实现

  • FileChannel:从文件中读写数据

  • DatagramChannel:通过UDP读写网络中的数据

  • SocketChannel:通过TCP读写网络中的数据

  • ServerSocketChannel:监听新进来的TCP连接,像Web服务器那样。对每一个新进来的连接都会创建一个SocketChannel

Scatter/Gather

  • 分散(scatter)从Channel中读取是指在读操作时将读取的数据写入多个buffer中。因此,Channel将从Channel中读取的数据“分散(scatter)”到多个Buffer中

  • 聚集(gather)写入Channel是指在写操作时将多个buffer的数据写入同一个Channel,因此,Channel 将多个Buffer中的数据“聚集(gather)”后发送到Channel

通过这样的方式可以方便数据的读取,当你想要获取整个数据的一部分的时候,通过这种方式可以很快的获取数据

ByteBuffer header = ByteBuffer.allocate(128);
ByteBuffer body   = ByteBuffer.allocate(1024);
ByteBuffer[] bufferArray = { header, body };
channel.read(bufferArray);

read()方法按照buffer在数组中的顺序将从channel中读取的数据写入到buffer,当一个buffer被写满后,channel紧接着向另一个buffer中写

transferFrom、transferTo

实现两个Channel之间相互连接,数据传递

    public static void trainforNio() {
        RandomAccessFile fromFile=null;
        RandomAccessFile toFile=null;
        try {

            fromFile = new RandomAccessFile("src/nio.txt", "rw");
            // channel获取数据
            FileChannel fromChannel = fromFile.getChannel();
            toFile = new RandomAccessFile("src/toFile.txt", "rw");
            FileChannel toChannel = toFile.getChannel();
            System.out.println(toChannel.size());
              //position处开始向目标文件写入数据,这里是toChannel
            long position = toChannel.size();
            long count = fromChannel.size();
            toChannel.transferFrom(fromChannel, position, count);
            System.out.println(toChannel.size());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fromFile != null) {
                    fromFile.close();
                }
                if (toFile != null) {
                    toFile.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

transferFrom、transferTo作用是一样的,只是一个是tochannal调用,一个是fromchannnal调用

在实际的运用中可能存在源通道的剩余空间小于 count 个字节,则所传输的字节数要小于请求的字节数

在SoketChannel的实现中,SocketChannel只会传输此刻准备好的数据(可能不足count字节)。因此,SocketChannel可能不会将请求的所有数据(count个字节)全部传输到FileChannel中

看官一定要仔细看我栗子中的注释

Buffer

Buffer是一个缓存区,其会将Channel中的数据存储起来

Buffer的实现

  • ByteBuffer

  • CharBuffer

  • DoubleBuffer

  • FloatBuffer

  • IntBuffer

  • LongBuffer

  • ShortBuffer

  • MappedByteBuffer

capacity,position,limit

在讲解该主题之前,首先要明白读模式和写模式,无论是Channel还是Buffer都存在这两种模式,要理解这两种模式,第一步要明确主题是哪一个,是Channel还是Buffer。举个栗子,主角是Channel,读模式的含义就是从Buffer中获取数据,写模式就是将数据写入Buffer,对于Buffer则是相反。搞清楚这一点,理解下面的就要相对清楚一点

  • capacity:作为一个内存块,其就代表了当前Buffer能最多暂存多少数据量,存储的数据类型则是根据上面的Buffer对象类型,一旦Buffer满了,需要将其清空(通过读数据或者清除数据)才能继续写数据往里写数据

  • position:代表当前数据读或写处于那个位置。读模式:被重置从0开始,最大值可能为capacity-1或者limit-1,写模式:被重置从0开始,最大值为limit-1

  • limit:最多能往Buffer里写多少数据,limit大小跟数据量大小和capacity有关,读模式:数据量>capacity时,limit=capacity,数据量=capacity时,limit=capacity,数据量<capacity时,limit<capacity,写模式:limit<=capacity

import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class Method {
    public static void nio() {
        RandomAccessFile aFile = null;
        try {

            aFile = new RandomAccessFile("src/nio.txt", "rw");
            // channel获取数据
            FileChannel fileChannel = aFile.getChannel();
            // 初始化Buffer,设定Buffer每次可以存储数据量
            // 创建的Buffer是1024byte的,如果实际数据本身就小于1024,那么limit就是实际数据大小
            ByteBuffer buf = ByteBuffer.allocate(1024);
            // channel中的数据写入Buffer
            int bytesRead = fileChannel.read(buf);
            System.out.println(bytesRead);

            while (bytesRead != -1) {
                // Buffer切换为读取模式
                buf.flip();
                // 读取数据
                while (buf.hasRemaining()) {
                    System.out.print((char) buf.get());
                }
                // 清空Buffer区
                buf.compact();
                // 继续将数据写入缓存区
                bytesRead = fileChannel.read(buf);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (aFile != null) {
                    aFile.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    public static void main(String[] args) {
        Method.nio();
    

Buffer读写数据步骤

  1. 写入数据到Buffer(fileChannel.read(buf))

  2. 调用flip()方法(buf.flip())

  3. 从Buffer中读取数据(buf.get())

  4. 调用clear()方法或者compact()方法(buf.compact())

Buffer方法

flip():将Buffer读模式切换到写模式,并且将position制为0

clear():清空整个缓冲区

compact():只会清除已经读过的数据。任何未读的数据都被移到缓冲区的起始处,新写入的数据将放到缓冲区未读数据的后面

allocate(1024):初始化Buffer,设定的值就决定capacity值的大小

rewind():将position设回0,所以你可以重读Buffer中的所有数据。limit保持不变,仍然表示能从Buffer中读取多少个元素(byte、char等)

mark()与reset():通过调用Buffer.mark()方法,可以标记Buffer中的一个特定position。之后可以通过调用Buffer.reset()方法恢复到这个position

equals():当满足下面三个条件时,两个Buffer才是相等

  • 有相同的类型(byte、char、int等)

  • Buffer中剩余的byte、char等的个数相等

  • Buffer中所有剩余的byte、char等都相同

只比较的是剩余的数据

compareTo():满足下列条件,则认为一个Buffer“小于”另一个Buffer

  • 第一个不相等的元素小于另一个Buffer中对应的元素

  • 所有元素都相等,但第一个Buffer比另一个先耗尽(第一个Buffer的元素个数比另一个少)

Selector

Selector允许单线程处理多个 Channel。如果你的应用打开了多个连接(通道),但每个连接的流量都很低,使用Selector就会很方便

关于selector的知识内容比较多了,我打算在下一期进行详细说明

更多内容可以关注微信公众号,或者访问AppZone网站

http://7xp64w.com1.z0.glb.clouddn.com/qrcode_for_gh_3e33976a25c9_258.jpg

相关文章:

  • Microsoft.Web.Redis.RedisSessionStateProvider
  • 【ZZ】如何选择适合自己项目的编程语言
  • 使用css3属性处理单词的换行和断词
  • Linux中文显示设置
  • Spark RDD API详解(一) Map和Reduce
  • 【HTML5】Server-Sent服务器发送事件
  • 阿里云centos 6.5 32位安装可视化界面的方法
  • Android 内容提供者的实现
  • SQL注入攻防入门详解
  • 关于Linux cached内存简析
  • abrtd是什么进程
  • linux下定时任务的使用
  • chrome远程调试真机上的app
  • I.MX6 boot from Micro SD
  • iOS开发多线程篇 03 —线程安全
  • 【跃迁之路】【585天】程序员高效学习方法论探索系列(实验阶段342-2018.09.13)...
  • co模块的前端实现
  • CSS3 聊天气泡框以及 inherit、currentColor 关键字
  • ES6核心特性
  • in typeof instanceof ===这些运算符有什么作用
  • JavaScript的使用你知道几种?(上)
  • java概述
  • nginx(二):进阶配置介绍--rewrite用法,压缩,https虚拟主机等
  • Vue2 SSR 的优化之旅
  • WinRAR存在严重的安全漏洞影响5亿用户
  • 翻译--Thinking in React
  • 构建二叉树进行数值数组的去重及优化
  • 让你的分享飞起来——极光推出社会化分享组件
  • 小程序开发中的那些坑
  • 掌握面试——弹出框的实现(一道题中包含布局/js设计模式)
  • AI算硅基生命吗,为什么?
  • MPAndroidChart 教程:Y轴 YAxis
  • Redis4.x新特性 -- 萌萌的MEMORY DOCTOR
  • Salesforce和SAP Netweaver里数据库表的元数据设计
  • 积累各种好的链接
  • 你学不懂C语言,是因为不懂编写C程序的7个步骤 ...
  • ​secrets --- 生成管理密码的安全随机数​
  • ​水经微图Web1.5.0版即将上线
  • ​学习一下,什么是预包装食品?​
  • #NOIP 2014# day.1 生活大爆炸版 石头剪刀布
  • #pragam once 和 #ifndef 预编译头
  • #QT(智能家居界面-界面切换)
  • #stm32驱动外设模块总结w5500模块
  • #Z0458. 树的中心2
  • (02)Cartographer源码无死角解析-(03) 新数据运行与地图保存、加载地图启动仅定位模式
  • (39)STM32——FLASH闪存
  • (Java实习生)每日10道面试题打卡——JavaWeb篇
  • (编程语言界的丐帮 C#).NET MD5 HASH 哈希 加密 与JAVA 互通
  • (附源码)计算机毕业设计ssm基于Internet快递柜管理系统
  • (十三)Maven插件解析运行机制
  • (转)eclipse内存溢出设置 -Xms212m -Xmx804m -XX:PermSize=250M -XX:MaxPermSize=356m
  • (转)linux自定义开机启动服务和chkconfig使用方法
  • (转)scrum常见工具列表
  • (转)负载均衡,回话保持,cookie
  • .bat批处理(六):替换字符串中匹配的子串