java管道piped输入流与输出流应用场景案例分析

目录
  • 前言
  • 原理简介
  • 使用场景概述
  • 实际应用
    • 案例一:EXCEL文件导出功能
    • 案例二:XML文件数据传输
  • 结语

前言

PipedInputStream 和 PipedOutputStream 设计用来解决跨线程的字节数据传输。它们总是成对出现的,而在使用上,也只能 工作在两个不同的线程上,在一个线程里使用管道输入和输出流可能会造成死锁。网上有很多介绍这两个存在于 io 包下的 api。却几乎 找不到一个写 PipedInputStream 的使用场景的,所以本文结合实际业务,来聊一聊 PipedInputStream 的应用。

原理简介

我们知道,输出流写数据,输入流读数据,PipedInputStream 和 PipedOutputStream 也一样,在 PipedOutputStream 的内部有一个 PipedInputStream 类型的 sink属性,用来接收 PipedOutputStream 写入的字节数据。

而在 PipedInputStream 内部,定义了一个默认为 1024 大小的字节数组 buffer,作为数据传输的缓冲区。这样一来,就变成了 PipedOutputStream 往 buffer 里写数据,当写满了 buffer 时,便使用 notifyAll() 唤醒读数据的线程可以读数据了,然后阻塞 1s 后继续尝试写数据。

PipedInputStream 从 buffer 里读数据,当数据读完 buffer 为空时,便 notifyAll() 唤醒写的线程可以写数据了,然后阻塞 1s 后继续尝试读数据。

PipedOutputStream 端数据写完后,调用 close() 方法,会标记 PipedInputStream 里的 closedByWriter=true。此时,从 buffer 读取数据,会返回 -1。标识了数据读完到达了流的末尾了。

使用场景概述

public static void main(String[] args) {
        try (PipedOutputStream out = new PipedOutputStream();
             PipedInputStream in = new PipedInputStream(out)) {
            new Thread(() -> {
                try {
                    out.write("hello kl".getBytes(StandardCharsets.UTF_8));
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }).start();
            int receive;
            while ((receive = in.read()) != -1) {
                System.err.print((char) receive);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

上面代码演示了,在一个线程里写数据,然后在 main 线程读数据的场景,完成了跨线程的数据传输。写到这里,都挺干巴巴的,很多人看了后肯定也不知道它到底能干啥,有啥作用,继续往下看。

实际应用

简单的理解了原理后,写了一个简单的演示 demo,但是 demo 不能说明啥问题,那从一个线程传输字节到另一个线程到底有啥用呢?博主,简单的的总结下:通过 java 应用生成文件,然后需要将文件上传到云端的场景,都可以用管道流。相同的业务场景,在没了解管道流之前,都是先将文件写入到本地磁盘,然后从文件磁盘读出来上传到云盘。了解这个后,可以脑补出很多的业务场景了(真实业务场景,都是博主遇到过的),比如:

案例一:EXCEL 文件导出功能

之前有一个文件导出的功能,但是因为,导出的文件比较大,导出下载完的时间非常长,所以,设计成了,页面点击导出后,后台触发导出任务,然后将mysql 中的数据根据导出条件查询出来,生成 Excel文件,然后将文件上传到 oss,最后像触发导出任务的人的钉钉发一个下载文件的链接。之前的做法,正如上面所言,先将文件写到本地,然后从本地目录读出来上传到 oss,下面演示下管道流一步到位的方式:

public static void main(String[] args) {
        try (PipedOutputStream out = new PipedOutputStream();
             PipedInputStream in = new PipedInputStream(out)) {
            new Thread(() -> {
                Listdatabase = new LinkedList<>();
                try {
                    //文件生成
                    ExcelUtils.getInstance().exportObjects2Excel(database,out);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }).start();
            //文件上传
            ossClient.putObject("test","test.xlsx",in);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

案例二:XML 文件数据传输

此类需求常见于和银行以及金融机构对接时,要求上报一些 xml 格式的数据,给到指定的 ftp、或是 oss 的某个目录下,用于对账。其实从文件上传的场景来说,和上面的案例一是一样。也是我总结的那样,在内存里生成文件,然后上传到云端,伪代码如下:

public static void main(String[] args) {
        try (PipedOutputStream out = new PipedOutputStream();
             PipedInputStream in = new PipedInputStream(out)) {
            new Thread(() -> {
                Listdatabase = new LinkedList<>();
                try(GZIPOutputStream gzipOut = new GZIPOutputStream(out)) {
                    Marshaller marshaller = JAXBContext.newInstance(Object.class).createMarshaller();
                    marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
                    marshaller.marshal(database,gzipOut);
                } catch (IOException | JAXBException e) {
                    e.printStackTrace();
                }
            }).start();
            //文件上传
            ossClient.putObject("test","test.xml.gz",in);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

总体来说,和案例一没啥区别,只是案例二多了一步压缩操作,最终上传的文件是一个 gzip 的压缩包,压缩包内是 xml 文件。用这种方式可以大大减少文件的体积、提升上传的速度

结语

PipedInputStream 和 PipedOutputStream 设计用来解决跨线程的字节数据传输。在实际业务需求中,当需要在内存中生成文件然后上传到云端时,请记得使用管道流

以上就是java管道piped输入流与输出流应用场景案例分析的详细内容,更多关于java管道piped输入流与输出流应用的资料请关注我们其它相关文章!

(0)

相关推荐

  • 举例讲解Java中Piped管道输入输出流的线程通信控制

    PipedOutputStream和PipedInputStream 在java中,PipedOutputStream和PipedInputStream分别是管道输出流和管道输入流. 它们的作用是让多线程可以通过管道进行线程间的通讯.在使用管道通信时,必须将PipedOutputStream和PipedInputStream配套使用. 使用管道通信时,大致的流程是:我们在线程A中向PipedOutputStream中写入数据,这些数据会自动的发送到与PipedOutputStream对应的Pip

  • java多线程编程之管道通信详解

    上一章节讲了wait/notify通信,这一节我们来探讨使用管道进行通信. java中提供了IO流使我们很方便的对数据进行操作,pipeStream是一种特殊的流,用于不同线程间直接传送数据.一个线程将数据发送到输出管道,另一个线程从输入管道读取数据.通过管道实现通信不需要借助临时文件这类东西. java中提供了四个类使得线程间可以通信: ①字节流:PipeInputStream,PipedOutputStream ②字符流:PipedReader,PipedWriter 下面我们看看字节流的实

  • Java输入输出流的使用详细介绍

    1.什么是IO Java中I/O操作主要是指使用Java进行输入,输出操作. Java所有的I/O机制都是基于数据流进行输入输出,这些数据流表示了字符或者字节数据的流动序列.Java的I/O流提供了读写数据的标准方法.任何Java中表示数据源的对象都会提供以数据流的方式读写它的数据的方法. Java.io是大多数面向数据流的输入/输出类的主要软件包.此外,Java也对块传输提供支持,在核心库 java.nio中采用的便是块IO. 流IO的好处是简单易用,缺点是效率较低.块IO效率很高,但编程比较

  • 详解PipedInputStream和PipedOutputStream_动力节点Java学院整理

    java 管道介绍 在java中,PipedOutputStream和PipedInputStream分别是管道输出流和管道输入流. 它们的作用是让多线程可以通过管道进行线程间的通讯.在使用管道通信时,必须将PipedOutputStream和PipedInputStream配套使用. 使用管道通信时,大致的流程是:我们在线程A中向PipedOutputStream中写入数据,这些数据会自动的发送到与PipedOutputStream对应的PipedInputStream中,进而存储在Piped

  • Java inputstream和outputstream使用详解

    计算机在进行I/O时都是以流的形式来进行,Java中所有流的相关操作的类,都继承自以下四个抽象类. 输入流 输出流 字节流 InputStream OutputStream 字符流 Reader Writer InPutStream的实现 import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java

  • java管道piped输入流与输出流应用场景案例分析

    目录 前言 原理简介 使用场景概述 实际应用 案例一:EXCEL文件导出功能 案例二:XML文件数据传输 结语 前言 PipedInputStream 和 PipedOutputStream 设计用来解决跨线程的字节数据传输.它们总是成对出现的,而在使用上,也只能 工作在两个不同的线程上,在一个线程里使用管道输入和输出流可能会造成死锁.网上有很多介绍这两个存在于 io 包下的 api.却几乎 找不到一个写 PipedInputStream 的使用场景的,所以本文结合实际业务,来聊一聊 Piped

  • Java线程通信中关于生产者与消费者案例分析

    相关方法: wait():一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器. notify():一旦执行此方法,就会唤醒被wait的一个线程,如果有多个线程被wait,就唤醒优先级高的那个. notifyAll():一旦执行此方法,就会唤醒所有被wait的线程. 说明: 1.wait(),notify(),notifyAll()三个方法必须使用在同步代码块或同步方法中. 2.wait(),notify(),notifyAll()三个方法的调用者必须是同步代码块或同步方法中的同步监视器.

  • ES2020 已定稿,真实场景案例分析

    近年来,JavaScript 的发展非常迅速. 尤其是在2015 年 ES6 发布之后,情况变得更好. 现在 许多新的特性被提议包括在 ES2020版本中.好消息是这些已经已经敲定. 现在,我们获得了最终定稿的功能清单,它们将在被批准发布之后出现在备受期待的 ES2020 中. 其中一些功能使我非常兴奋,因为在它们存在之前编写代码时遇到将会遇到很多麻烦. 让我们看看它们是什么吧! 可选链操作符(Optional Chaining Operator) 对我个人来说,这是 ES2020最令人兴奋的特

  • Java中包的概念和用法实战案例分析

    本文实例讲述了Java中包的概念和用法.分享给大家供大家参考,具体如下: 一 点睛 1 package的声明方法: package package名称[.package名称2.package名称3]; 2 包的导入方法如下: import package 包名称.类名称 3 如果一个项目之中有几百个类,一个个导入会比较麻烦,为了方便导入,可以使用"包名.*"的形式完成,例如: import java.io.*; 这里的"*"是通配符,表示该"包名"

  • Java中的字节,字符输出流与字节和字符输入流的简单理解

    目录 字节输出流OutputStream 字符输出流 字节输入流InputStream 字符输入流Reader 字节流和字符流的区别 总结 我先解释一下什么叫IO流: I:指的是InputStream,这是一个抽象类,最常用的子类是FileInputStream O:值得是OutputStream,这也是一个抽象类,最常用的子类是OutputStream 流:由于在进行文件操作的时候大多数是用的byte数据,这些数据并不是一次性写入(读取),而是像水龙头那样慢慢的流(想象一下你接水的场景) 废话

  • C++输入流和输出流 超级详细

    目录 1.简单介绍 2.C++输入流和输出流 1.简单介绍 C++ 又可以称为"带类的 C",即可以理解为 C++ 是 C 语言的基础上增加了面向对象(类和对象).在此基础上,学过 C 语言的读者应该知道,它有一整套完成数据读写(I/O)的解决方案: 使用 scanf() .gets() 等函数从键盘读取数据,使用 printf() .puts() 等函数向屏幕上输出数据: 使用 fscanf() .fgets() 等函数读取文件中的数据,使用 fprintf() .fputs() 等

  • Android中文件读写(输入流和输出流)操作小结

    1. Android中文件读写的原理: (1).所有文件的储存都是字节的储存. (2).在磁盘上保留的并不是文件的字符而是先把字符编码成字节,再储存这些字节到磁盘. (3).在读取文件(特别是文本文件)时,也是一个字节一个字节的读取以形成字节序列. 2. 字节流和字符流的区别: (1).字节流提供了处理任何类型的IO操作的功能,但它不能直接处理Unicode字符,字符流就可以. (2).字节流转换成字符流可以用InputStreamReader,OutputStreamWriter. 一般我们在

  • java中ThreadLocal的应用场景实例分析

    说到线程的安全,我们可以通过ThreadLocal来解决.但作为一种强大的变量,它的应用场景远不止如此.在各类的框架中,我们依然可以使用来对它们进行管理.同时在使用ThreadLocal时需要注意内存泄漏的问题.下面我们就这两点进行分析,并带来对应代码的展示. 1.各种框架中的应用 Spring框架的事务管理中使用ThreadLocal来管理连接,每个线程是单独的连接,当事务失败时不能影响到其他线程的事务过程或结果,还有大家耳闻目睹的ORM框架.Mybatis也是用ThreadLocal管理,S

  • Java文件字符输入流FileReader读取txt文件乱码的解决

    目录 Java文件字符输入流FileReader读取txt文件乱码 先上代码 控制台输出结果如下 原因是 运行之后的结果为 字符流读取UTF-8和写出txt文件乱码问题 话不多说,直接上图 解决 Java文件字符输入流FileReader读取txt文件乱码 先上代码 public class FileInAndOut { public static void main(String[] args) { //定义指定磁盘的文件的File对象 File file = new File("E:/大三下

  • Java各种锁在工作中使用场景和细节经验总结

    目录 1.synchronized 1.1.共享资源初始化 2.CountDownLatch 2.1.场景 2.2.实现 3.总结 1.synchronized synchronized 是可重入的排它锁,和 ReentrantLock 锁功能相似,任何使用 synchronized 的地方,几乎都可以使用 ReentrantLock 来代替,两者最大的相似点就是:可重入 + 排它锁,两者的区别主要有这些: ReentrantLock 的功能更加丰富,比如提供了 Condition,可以打断的加

随机推荐