我的编程空间,编程开发者的网络收藏夹
学习永远不晚

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

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

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

前言

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输入流与输出流应用的资料请关注编程网其它相关文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

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

下载Word文档到电脑,方便收藏和打印~

下载Word文档

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录