Java IO流

综述

流代表任何有能力产出数据的数据源对象,或者是有能力接受数据的接收端对象。
流的本质是数据传输,根据数据传输类型,将流分为各种类,分别操作。

1、装饰者模式

装饰,类似于装饰房间,每添加一件装饰物品,房间气氛都会改变,装饰者模式也起到这样的作用,动态的为一个对象添加其他功能,装饰者提供了一种灵活的方式来替换继承。

装饰者模式的框架如下:
在这里插入图片描述

  • Component,也就是我们需要装饰的对象,他是抽象的,定义了一些方法,实际上要装饰的就是它的方法。
  • ConcreteComponent,上述对象子类化后的对象,实现了抽象方法。
  • Decorator,装饰者,也继承了Component类,实现了抽象方法。
  • ConcreteDecoratorA & B,具体的装饰类,覆盖了Component方法,增加了装饰逻辑。

简单例子加深理解
①、被装饰的类

public abstract class Human {
    public abstract void wear();
    public abstract void walk();
}

②、装饰者

public abstract class Decorator extends Human{

    private Human human;

    public Decorator(Human human) {
        this.human = human;
    }

    @Override
    public void wear() {
        human.wear();
    }

    @Override
    public void walk() {
        human.walk();
    }
}

③、定义三种装饰

public class Decorator_zero extends Decorator {
    public Decorator_zero(Human human) {
        super(human);
    }

    @Override
    public void wear() {
        super.wear();
        goHome();
    }

    @Override
    public void walk() {
        super.walk();
        outHome();
    }

    public void goHome(){
        System.out.println("回家找衣服");
    }

    public void outHome(){
        System.out.println("出门散步");
    }
}
public class Decorator_first extends Decorator {
    public Decorator_first(Human human) {
        super(human);
    }

    @Override
    public void wear() {
        super.wear();
        goRoom();
    }

    @Override
    public void walk() {
        super.walk();
        goGarden();
    }

    public void goRoom(){
        System.out.println("去房间找衣服");
    }

    public void goGarden(){
        System.out.println("去花园");
    }
}
public class Decorator_sec extends Decorator{
    public Decorator_sec(Human human) {
        super(human);
    }

    @Override
    public void wear() {
        super.wear();
        findClothes();
    }

    @Override
    public void walk() {
        super.walk();
        walking();
    }

    public void findClothes(){
        System.out.println("找到衣服并穿上");
    }

    public void walking(){
        System.out.println("已经到花园开始散步");
    }
}

④、定义被装饰者,被装饰者初始状态有些自己的装饰

public class Person extends Human {


    @Override
    public void wear() {
        System.out.println("穿衣服");
    }

    @Override
    public void walk() {
        System.out.println("出去散步");
    }
}

⑤、测试

public class DecoratorMain {
    public static void main(String[] args) {
        Decorator decorator = new Decorator_sec(new Decorator_first(new Decorator_zero(new Person())));
        decorator.wear();
        decorator.walk();
        /**
         * 穿衣服
         * 回家找衣服
         * 去房间找衣服
         * 找到衣服并穿上
         * 出去散步
         * 出门散步
         * 去花园
         * 已经到花园开始散步
         */
    }
}
2、IO流总览

Java的IO流采用了装饰者模式对不同功能的流进行划分,我们可以动态的装饰这些Stream以获得想要的结果。

输入与输出
这里的输入与输出是基于程序而言的,从外部读取到程序中的叫输入,从程序写出到其他地方的叫输出。

4个基本的抽象流类型,所有的流都继承这四个

输入流输出流
字节流InputStreamOutputStream
字符流ReaderWriter

IO流的特性

  • 先进先出,最先写入输入流的数据最先被输入流读取到;
  • 顺序存取,可以一个接一个的向流中写入一个字节,读取时也将按照写入的顺序读取一串字节,不能随机访问。
  • 只读或只写,每个流只能是输入或输出的一种。
3、IO读写原理理解

用户程序进行IO的读写,基本上都会用到read&write两大系统调用。read系统调用并不是直接把数据从物理设备读到内存,而是把数据从内核缓冲区复制到用户缓冲区(也可以叫做进程缓冲区);write系统调用也不是直接把数据从内存写入物理设备,而是把数据从进程缓冲区复制到内核缓冲区。这两个操作都不负责数据在内核缓冲区与物理设备之间的交换,底层的数据交换是由操作系统完成的。
在这里插入图片描述
DMA,全称叫直接内存存取(Direct Memory Access),是一种允许外围设备(硬件子系统)直接访问系统主内存的机制。基于 DMA 访问方式,系统主内存与硬件设备的数据传输可以省去CPU 的全程调度

内核缓冲区与进程缓冲区
缓冲区的作用是为了减少频繁的系统IO调用,系统调用需要保存之前的进程数据和状态等信息,而结束调用之后还需要恢复之前的信息,为了减少这种损耗时间也损耗性能的系统调用于是出现了缓冲区。

有了缓冲区,系统调用read或write等待缓冲区到达一定数量的时候在进行系统调用,提高性能。至于什么时候读取和存储则由内核来决定不需要用户关心。
用户程序的IO操作大多数情况下并没有进行真正的IO操作,而是在读写自己的进程缓冲区。

4、四种IO模型
  • ①、同步阻塞IO(Blocking IO)
    在这里插入图片描述
    分析:当用户进程进行系统调用时,内核就开始了第一个阶段:准备数据到缓冲区,当数据都准备完成后将数据从内核缓冲区复制到用户缓冲区,这时返回成功指示,解除阻塞。
    例子:去餐厅吃饭->点餐->取餐->吃饭,这整个过程中什么都不干一直等待饭做好。

  • ②、同步非阻塞IO(Non-Blocking IO)
    在这里插入图片描述
    分析:在函数调用之后,如果没有数据函数立马返回,需要加入轮询逻辑,轮询如果有数据就返回数据。
    这种IO第一阶段(数据准备)并没有阻塞,只是第二阶段阻塞,比如,在等待饭做好的期间可以出去买其他的东西,而不必再一直等待。
    非阻塞IO的特点有如下几种:

    • 复制数据的整个过程,进程仍然是阻塞的;
    • 需要不断地轮询数据是否准备好了;
    • 能够在等待任务完成的期间执行其他任务;
    • 由于需要轮询,延迟会增加。
  • ③、IO多路复用模型(I/O multiplexing)
    IO多路复用模型解决了NIO中需要不断轮询的问题。IO多路复用技术的原理是select/epoll系统调用,单个线程不断地轮询select/epoll负责的成百上千的socket连接,当某个或某些socket有数据到达了,就返回这些可以读写的连接,它的好处在于通过一次select/epoll系统调用就查询到一个或多个可读写的网络连接。
    在这里插入图片描述
    分析:在这种模式中,首先将连接注册到select/epoll的可查询socket列表中,然后进行select/epoll系统调用,之后才是read操作。
    流程大致如下:

    • 首先查询select/epoll系统调用,查询可读的连接,内核会查询所有select的可查询socket列表,当其中任何一个socket的数据准备好了select就会返回。
    • 用户线程获取到连接之后发起read调用,用户线程阻塞内核开始复制数据,从内核缓冲区复制到进程缓冲区,然后内核返回结果。
    • 用户线程接触阻塞状态,用户线程也才真正获取到数据。
      IO多路复用模型其实也需要轮询,也即是不断地轮询select/epoll,获取到可以及进行IO操作的连接,IO多路复用技术可以处理成千上万个连接,而不必每个连接都创建一个线程,大大减小了系统的开销。IO多路复用是阻塞在select,epoll这样的系统调用之上,而没有阻塞在真正的I/O系统调用如recvfrom之上。

例子:我们点餐之后不用过一段时间就去询问店家是否做好,而是拿到一个牌子,当店家做好之后牌子回响铃提示,我们再去取就行了。

本质上select/epoll系统调用是同步IO,也是阻塞的,需要在读写事件就绪后,自己负责读写,这个读写过程是阻塞的。

  • ④、异步IO模型(Asynchronous IO)
    在这里插入图片描述
    分析:当用户线程调用了read之后就可以立即去做其他事情了,而不必等待,用户线程步不阻塞,直到IO执行的两个阶段都完成之后,内核会给用户进程发送通知,告诉用户进程操作已经完成了。

例子:如果饿了可以直接点外卖,不用再去店里等了,点完之后等待送达即可。

参考文章:
Linux 五种IO模型
10分钟看懂, Java NIO 底层原理
【Linux基础】Linux的5种IO模型详解

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值