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

Java多线程和IO流怎么应用

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

北京

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

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

看不清楚,换张图片

免费获取短信验证码

Java多线程和IO流怎么应用

这篇文章主要介绍“Java多线程和IO流怎么应用”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Java多线程和IO流怎么应用”文章能帮助大家解决问题。

Java多线程和流的应用

最近看到了一个例子,是使用多线程的方式下载文件,感觉很有趣,探索了一下,并且尝试了使用多线程进行本地复制文件。写完之后,发现了这两个其实很相似,无论是本地文件复制,还是网络多线程下载,对于流的使用都是一样的。对于本地文件系统来说,输入流就是从本地文件系统的一个文件来获取,对于网络资源来说,是从远处服务器上的一个文件来获取。

注: 虽然这个多线程下载的代码,很多人都写过了,不过应该不是所有人都能理解吧,我这里就再写一遍,哈。

使用多线程的一个显而易见的好处就是:利用空闲的 CPU,加快速度。 但是注意不是线程越多越好,虽然好像n个线程一起下载,每个线程下载一小部分,下载时间就会变为1/n 了。这是很浅显的认识,就好像一个人盖一个房子需要100天,难道10000个人,只需要1/10 天一样了?(这是一个夸张的说法,哈哈!)

线程之间的切换也是需要系统开销的,线程的数目还是要控制在一个合理的范围内才行。

RamdomAccessFile

这个类比较独特,它即可以从文件中读取数据,也可以向文件中写入数据。但是它不是 OutputStream 和 InputStream 的子类,它是实现了这两个接口 DataOutputDataInput 的一个类。

API 中的介绍:

该类的实例支持读取和写入随机访问文件。 随机访问文件的行为类似于存储在文件系统中的大量字节。 有一种游标,或索引到隐含的数组,称为文件指针 ; 输入操作读取从文件指针开始的字节,并使文件指针超过读取的字节。 如果在读/写模式下创建随机访问文件,则输出操作也可用; 输出操作从文件指针开始写入字节,并将文件指针提前到写入的字节。 写入隐式数组的当前端的输出操作会导致扩展数组。 文件指针可以通过读取getFilePointer方法和由设置seek方法

所以,这个类最重要的就是 seek 方法了,使用 seek 方法,可以控制写入的位置,所以实现多线程就容易的多了。因此,无论是本地文件复制,还是网络多线程下载都需要这个类的作用。

具体思路是: 首先使用 RandomAccessFile 创建一个 File 对象,然后设置这个文件的大小。(对的,它可以直接设置文件的大小。)将这个文件设置成和要复制或下载的文件一样的。(虽然我们并没有向这个文件中写入数据,但是这个文件已经创建了。)将文件分为若干部分,使用线程复制或者下载每一部分的内容

这有点类似文件的覆盖,如果一个已经存在的文件,从这个文件的头部开始写入数据,一直写到文件的尾部,那么原来的文件就不存在了,变成了写入的新文件。

设置文件大小:

private static void createOutputFile(File file, long length) throws IOException {try (   RandomAccessFile raf = new RandomAccessFile(file, "rw")){raf.setLength(length);}}

用图片来说明: 这个图表示一个 8191 字节大小的文件: 每一个部分大小是:8191 / 4 = 2047字节

将这个文件的分为四个部分,每一个部分使用一个线程进行复制或下载,其中每一个箭头代表一个线程的开始下载位置。我特意将最后一个部分,没有设置为 1024 byte,这是因为文件很少是正好能被 1024 byte 整除的。(之所以使用 1024 byte,是因为我每次会读取 1024 byte,如果读取到 1024 byte的话, 否则写入读取到的相应字节数)。

按照这个示意图,每一个线程下载 2047 byte,那么总共下载的字节数是:2047 * 4 = 8188 字节 < 8191 字节(文件的总大小) 所以这就产生了一个问题,下载的字节数少于总字节数,这就是问题了,所以必须要下载的字节数大于总字节数才行。(多了没有关系,因为多下载的部分,会被后面的给覆盖掉,不会产生了问题。

所以每个部分的大小应该是:8191 / 4 + 1 = 2048 字节。(这样四部分的大小相加是超过总大小的,不会发生数据的丢失问题。)

所以,这里这个加 1 是很有必要的。

long size = len / FileCopyUtil.THREAD_NUM + 1;

Java多线程和IO流怎么应用

每个线程下载完成的位置(右边) 每个线程,只复制下载自己的那部分,所以不需要全部下载完所有的内容,所以读取文件数据并写入文件的部分,会多加一个判断。

这里增加一个计数器:curlen。它表示是当前复制或者下载的长度,然后每次读取后和 size(每部分的大小)进行比较,如果 curlen 大于 size 就表示相应的部分下载完成了(当然了,这些都要在数据没有读取完的条件下判断)。

try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(targetFile));RandomAccessFile raf = new RandomAccessFile(outputFile, "rw")){bis.skip(position);  raf.seek(position);  int hasRead = 0;byte[] b = new byte[1024];long curlen = 0;while(curlen < size && (hasRead = bis.read(b)) != -1) {raf.write(b, 0, hasRead);curlen += (long)hasRead;}System.out.println(n+" "+position+" "+curlen+" "+size);} catch (IOException e) {e.printStackTrace();}

Java多线程和IO流怎么应用

还有需要注意的是,每个线程下载的时候都要: 1. 输出流设置文件指针的位置。 2. 输入流跳过不需要读取的字节。

这是很重要的一步,应该是很好理解的。

bis.skip(position);  raf.seek(position);

多线程本地文件复制(完整代码)

package dragon;import java.io.BufferedInputStream;import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;import java.io.RandomAccessFile;public class FileCopyUtil {//设置一个常量,复制线程的数量private static final int THREAD_NUM = 4;private FileCopyUtil() {}public static void transferFile(String targetPath, String outputPath) throws IOException {File targetFile = new File(targetPath);File outputFilePath = new File(outputPath);if (!targetFile.exists() || targetFile.isDirectory()) {   //目标文件不存在,或者是一个文件夹,则抛出异常throw new FileNotFoundException("目标文件不存在:"+targetPath);}if (!outputFilePath.exists()) {     //如果输出文件夹不存在,将会尝试创建,创建失败,则抛出异常。if(!outputFilePath.mkdir()) {throw new FileNotFoundException("无法创建输出文件:"+outputPath);}}long len = targetFile.length();File outputFile = new File(outputFilePath, "copy"+targetFile.getName());createOutputFile(outputFile, len);    //创建输出文件,设置好大小。long[] position = new long[4];//每一个线程需要复制文件的起点long size = len / FileCopyUtil.THREAD_NUM + 1;for (int i = 0; i < FileCopyUtil.THREAD_NUM; i++) {position[i] = i*size;copyThread(i, position[i], size, targetFile, outputFile);}}//创建输出文件,设置好大小。private static void createOutputFile(File file, long length) throws IOException {try (   RandomAccessFile raf = new RandomAccessFile(file, "rw")){raf.setLength(length);}}private static void copyThread(int i, long position, long size, File targetFile, File outputFile) {int n = i;   //Lambda 表达式的限制,无法使用变量。new Thread(()->{try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(targetFile));RandomAccessFile raf = new RandomAccessFile(outputFile, "rw")){bis.skip(position);  //跳过不需要读取的字节数,注意只能先后跳raf.seek(position);  //跳到需要写入的位置,没有这句话,会出错,但是很难改。int hasRead = 0;byte[] b = new byte[1024];long curlen = 0;while(curlen < size && (hasRead = bis.read(b)) != -1) {raf.write(b, 0, hasRead);curlen += (long)hasRead;}System.out.println(n+" "+position+" "+curlen+" "+size);} catch (IOException e) {e.printStackTrace();}}).start(); }}

多线程网络下载(完整代码)

package dragon;import java.io.BufferedInputStream;import java.io.File;import java.io.FileNotFoundException;import java.io.IOException;import java.io.RandomAccessFile;import java.net.URL;import java.net.URLConnection;public class FileDownloadUtil {//下载线程数private static final int THREAD_NUM = 4;public static void transferFile(String url, String output) throws IOException {init(output);URL resource = new URL(url);URLConnection connection = resource.openConnection();//获取文件类型String type = connection.getContentType();if (type != null) {type = "."+type.split("/")[1];} else {type = "";}//创建文件,并设置长度。long len = connection.getContentLength();String filename = System.currentTimeMillis()+type;try (RandomAccessFile raf = new RandomAccessFile(new File(output, filename), "rw")){raf.setLength(len);}//为每一个线程分配相应的下载其实位置long size = len / THREAD_NUM + 1;  long[] position = new long[THREAD_NUM];File downloadFile = new File(output, filename);//开始下载文件: 4个线程download(url, downloadFile, position, size);}private static void download(String url, File file, long[] position, long size) throws IOException {//开始下载文件: 4个线程for (int i = 0 ; i < THREAD_NUM; i++) {position[i] = i * size;   //每一个线程下载的起始位置int n = i;  // Lambda 表达式的限制,无法使用变量new Thread(()->{URL resource = null;URLConnection connection = null;try {resource = new URL(url);connection = resource.openConnection();} catch (IOException e) {e.printStackTrace();}try (BufferedInputStream bis = new BufferedInputStream(connection.getInputStream());RandomAccessFile raf = new RandomAccessFile(file, "rw")){  //每个流一旦关闭,就不能打开了raf.seek(position[n]);     //跳到需要下载的位置bis.skip(position[n]);   //跳过不需要下载的部分int hasRead = 0;byte[] b = new byte[1024];long curlen = 0;while(curlen < size && (hasRead = bis.read(b)) != -1) {raf.write(b, 0, hasRead); curlen += (long)hasRead;}System.out.println(n+" "+position[n]+" "+curlen+" "+size);} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}) .start();}}private static void init(String output) throws FileNotFoundException {File path = new File(output);if (!path.exists()) {if (!path.mkdirs()) {throw new FileNotFoundException("无法创建输出路径:"+output);}} else if (path.isFile()) {throw new FileNotFoundException("输出路径不是一个目录:"+output);}}}

测试代码及结果

因为这个多线程文件复制和多线程下载是很相似的,所以就放在一起测试了。我也想将两个写在一个类里面,这样可以做成方法的重载调用。 文件复制的第一个参数可以是 String 或者 URI。 使用这个作为目标文件的参数。

public File(URI uri)

网络文件下载的第一个参数,可以使用 String 或者是 URL。 不过,因为先写的这个文件复制,后写的多线程下载,就没有做这部分。不过现在这样功能也达到了,可以进行本地文件的复制(多线程)和网络文件的下载(多线程)。

package dragon;import java.io.IOException;public class FileCopyTest {public static void main(String[] args) throws IOException {//复制文件long start = System.currentTimeMillis();try {FileCopyUtil.transferFile("D:\\DB\\download\\timg.jfif", "D:\\DBC");} catch (IOException e) {e.printStackTrace();}long time = System.currentTimeMillis()-start;System.out.println("time: "+time);//下载文件start = System.currentTimeMillis();FileDownloadUtil.transferFile("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1578151056184&di=594a34f05f3587c31d9377a643ddd72e&imgtype=0&class="lazy" data-src=http%3A%2F%2Fn.sinaimg.cn%2Fsinacn%2Fw1600h2000%2F20180113%2F0bdc-fyqrewh6850115.jpg", "D:\\DB\\download");System.out.println("time: "+(System.currentTimeMillis()-start));}}

运行截图: 注意:这里这个时间并不是复制和下载需要的时间,实际上它没有这个功能!

注意:虽然两部分代码是相同的,但是第三列数字,却不是完全相同的,这个似乎是因为本地和网络得区别吧。但是最后得文件是完全相同的,没有问题得。(我本地文件复制得是网络下载得那张图片,使用图片进行测试有一个好处,就是如果错了一点(字节数目不对),这个图片基本上就会产生问题。)

Java多线程和IO流怎么应用

产生错误之后的图片: 图片无法正常显示,会出现很多的问题,这就说明一定是代码写错了。

关于“Java多线程和IO流怎么应用”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识,可以关注编程网行业资讯频道,小编每天都会为大家更新不同的知识点。

免责声明:

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

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

Java多线程和IO流怎么应用

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

下载Word文档

猜你喜欢

Java多线程和IO流怎么应用

这篇文章主要介绍“Java多线程和IO流怎么应用”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Java多线程和IO流怎么应用”文章能帮助大家解决问题。Java多线程和流的应用最近看到了一个例子,是使
2023-07-06

详解Java多线程和IO流的应用

这篇文章主要介绍了详解Java多线程和IO流的应用,无论是本地文件复制,还是网络多线程下载,对于流的使用都是一样的,需要的朋友可以参考下
2023-05-15

java多线程在项目中怎么应用

Java多线程在项目中的应用主要有以下几个方面:1. 提高程序的并发性:多线程可以同时处理多个任务,提高程序的并发性,使得程序的执行效率更高。2. 实现异步操作:多线程可以实现异步操作,将耗时的操作放在后台线程中执行,避免阻塞主线程,提高用
2023-09-29

Java多线程怎么用

这篇文章主要介绍了Java多线程怎么用,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。一、概念1.进程1.1进程:是一个正在进行中的程序,每一个进程执行都有一个执行顺序,该顺序
2023-06-17

python多线程怎么应用

本篇内容主要讲解“python多线程怎么应用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“python多线程怎么应用”吧!一、首先定义一个封装类,主要是保证PyGILState_Ensure,
2023-06-17

java多线程应用场景是什么

本篇内容主要讲解“java多线程应用场景是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“java多线程应用场景是什么”吧!本教程操作环境:windows7系统、java10版,DELL G3
2023-06-30

Java多线程开发工具之CompletableFuture怎么应用

这篇“Java多线程开发工具之CompletableFuture怎么应用”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Jav
2023-07-05

java中怎么使用io流读写文件

在Java中,可以使用IO流来读写文件。下面是一些常见的示例:1. 使用FileInputStream和FileOutputStream类来读写字节流文件:```javaFileInputStream fis = null;FileOutp
2023-08-26

java怎么使用线程池启动多线程

在 Java 中,可以使用线程池来启动多线程。以下是使用线程池启动多线程的示例代码:首先,需要导入 `java.util.concurrent.ExecutorService` 和 `java.util.concurrent.Executo
2023-09-15

java虚拟线程怎么应用

Java虚拟线程的应用Java虚拟线程提供高性能、可伸缩性和隔离性,非常适合I/O密集型、计算密集型和事件处理任务。通过轻量级的线程创建和销毁,虚拟线程可优化微服务、游戏开发和并发编程。JavaAPI提供易于使用的创建、管理和调度功能,而其用户空间实现则保证了效率和可扩展性。最佳实践包括避免阻塞、限制线程数量、使用线程池和监控性能。
java虚拟线程怎么应用
2024-04-11

java虚拟线程怎么应用

Java虚拟线程,即Java虚拟机(JVM)中的线程,可以通过以下几种方式应用:1. 多线程编程:Java提供了多线程编程的支持,可以通过创建线程对象、实现Runnable接口或继承Thread类来创建线程,并通过调用start()方法启动
2023-09-21

java守护线程怎么应用

Java守护线程是指在后台提供一种通用服务的线程,并且在所有非守护线程结束时自动退出。守护线程通常用于执行一些背景任务,如垃圾回收、内存管理等,以保证程序的正常运行。要创建守护线程,可以通过Thread类的setDaemon()方法来设置
2023-10-27

Java多线程的应用场景和应用目的实例分析

Java多线程的应用场景和应用目的实例分析,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。通俗的解释一下多线程先:多线程用于堆积处理,就像一个大土堆,一个推土机很慢,那么10个推
2023-06-19

java 多线程使用PipedOutStream和PipedInputStream

package test;import java.io.PipedInputStream;import java.io.PipedOutputStream;public class PipeTest {public static void
2023-06-02

java中多线程和线程安全是什么

这篇文章给大家分享的是有关java中多线程和线程安全是什么的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。什么是进程?电脑中时会有很多单独运行的程序,每个程序有一个独立的进程,而进程之间是相互独立存在的。比如下图中
2023-06-25

Java多线程之锁怎么使用

本篇内容介绍了“Java多线程之锁怎么使用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!首先强调一点:Java多线程的锁都是基于对象的,Ja
2023-07-05

怎么在Java中利用IO流复制文件

这期内容当中小编将会给大家带来有关怎么在Java中利用IO流复制文件,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。Java可以用来干什么Java主要应用于:1. web开发;2. Android开发;3.
2023-06-14

Java 多线程中的Runnable怎么用

Java 多线程中的Runnable怎么用,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。package com.thread;/** 注意:1. 如果要启动一个线程必须调用
2023-06-02

Java多线程应用方法有哪些

这篇文章主要讲解了“Java多线程应用方法有哪些”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java多线程应用方法有哪些”吧!Java多线程程序中经常用到的方法有以下几个:run(),st
2023-06-17

怎么在Java中使用IO流读写文件

本篇文章为大家展示了怎么在Java中使用IO流读写文件,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。1.读文件public class ReadFromFile { /** * 以字节为单位读
2023-05-31

编程热搜

  • 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动态编译

目录