java多线程实现复制功能并统计进度

业务描述

复制某目录下的一个大文件,要求使用10个线程同时工作。并且统计复制的完成度,类似于进度条的功能。

业务分析

步骤:

1、在使用多线程进行拷贝的时候,首先要知道文件的大小 然后根据线程的数量,计算出每个线程的工作的数量。需要一个拷贝的类,进行复制,初始化线程数组

2、创建一个统计文件复制进度的线程类。

3、拷贝线程。

4、由于Java的简单类型不能够精确的对浮点数进行运算,提供一个java工具类,对浮点数进行计算。

5、创建主函数类进行测试。

代码如下:

1package javasimple; 2 3import java.io.File; 4import java.io.FileNotFoundException; 5import java.io.IOException; 6import java.io.RandomAccessFile; 7import java.math.BigDecimal; 8 9public class ThreadCopyFile { 10 11 /** 12 * @param args 13 */ 14 public static void main(String[] args) { 15 Copyer copyer = new Copyer(); 16 copyer.copy(new File("E:\\gcfr\\hqreportnew.war"), "E:\\", 10); 17 } 18} 19/** 20 * 该类执行文件的拷贝功能 21 * @author haokui 22 * 23 */ 24class Copyer { 25 26 private CopyThread[] threads;// 存放所有拷贝线程的数组 27 28 /** 29 * 使用多线程去拷贝一个大文件, 1 在使用多线程进行拷贝的时候,首先要知道文件的大小 然后根据线程的数量,计算出每个线程的工作的数量 30 * 2.然后创建线程,执行拷贝的工作 31 * 32 * @param scrFile 33 * 源文件 34 * @param desPath 35 * 目标路径 36 * @param threadNum 37 * 要使用的线程数量 38 */ 39 public void copy(File srcFile, String desPath, int threadNum) { 40 // 1.取得文件的大小 41 long fileLeng = srcFile.length(); 42 System.out.println("文件大小:" + fileLeng); 43 44 // 2.根据线程数量,计算每个线程的工作量 45 long threadPerSize = fileLeng / threadNum; 46 47 // 3.计算出每个线程的开始位置和结束位置 48 long startPos = 0; 49 long endPos = threadPerSize; 50 51 // 取得目标文件的文件名信息 52 String fileName = srcFile.getName(); 53 String desPathAndFileName = desPath + File.separator + fileName; 54 55 // 初始化线程的数组 56 threads = new CopyThread[threadNum]; 57 58 for (int i = 0; i < threadNum; i++) { 59 // 由最后一个线程承担剩余的工作量 60 if (i == threadNum - 1) { 61 threads[i] = new CopyThread("拷贝线程" + i, srcFile, 62 desPathAndFileName, startPos, fileLeng); 63 } else { 64 // 创建一个线程 65 threads[i] = new CopyThread("拷贝线程" + i, srcFile, 66 desPathAndFileName, startPos, endPos); 67 } 68 startPos += threadPerSize; 69 endPos += threadPerSize; 70 71 } 72 73 // 创建统计线程 74 new ScheduleThread("统计线程", fileLeng,threads ); 75 } 76} 77/** 78 * 负责统计文件拷贝进度的线程 79 * @author haokui 80 * 81 */ 82class ScheduleThread extends Thread { 83 private long fileLength; // 文件的大小 84 private CopyThread[] threads;// 存放所有的拷贝线程的数组 85 86 /** 87 * 统计进度线程的构造方法 88 * 89 * @param name 90 * 线程的名字 91 * @param fileLeng 92 * 文件的长度 93 * @param threads 94 * 拷贝线程的数组 95 */ 96 public ScheduleThread(String name, long fileLength, CopyThread[] threads) { 97 super(name); 98 this.fileLength = fileLength; 99 this.threads = threads; 100 101 this.start(); 102 } 103 104 /** 105 * 判断所有的拷贝线程是否已经结束 106 * 107 * @return 是否结束 108 */ 109 private boolean isOver() { 110 if (threads != null) { 111 for (CopyThread t : threads) { 112 if (t.isAlive()) { 113 return false; 114 } 115 } 116 } 117 return true; 118 } 119 120 public void run() { 121 while (!isOver()) { 122 long totalSize = 0; 123 for (CopyThread t : threads) { 124 totalSize += t.getCopyedSize(); 125 } 126 /** 127 * 由于复制功能要比这些代码耗时,所以稍微延迟一下,不用计算的太频繁,最好是一个线程干完之后计算一次,这里就直接给延迟一下就ok,不做精确的处理了。 128 */ 129 try { 130 Thread.sleep(5); 131 } catch (InterruptedException e) { 132 e.printStackTrace(); 133 } 134 double schedule = Arith.div((double) totalSize, 135 (double) fileLength, 4); 136 System.err.println("文件的拷贝进度:===============>" + schedule * 100 137 + "%"); 138 } 139 System.err.println("统计线程结束了"); 140 } 141} 142/** 143 * 拷贝线程 144 * @author haokui 145 * 146 */ 147class CopyThread extends Thread { 148 private File srcFile;// 源文件的路径 149 private String desPath;// 目标路径 150 private long startPos; // 线程拷贝的开始位置 151 private long endPost;// 线程拷贝的结束位置 152 private long alreadyCopySize;// 线程已经拷贝的位置 153 154 private RandomAccessFile rin; // 读取文件的随机流 155 private RandomAccessFile rout;// 写入文件的随机流 156 157 /** 158 * 取得 线程已经拷贝文件的大小 159 * 160 * @return 线程已经拷贝文件的大小 161 */ 162 public long getCopyedSize() { 163 return alreadyCopySize - startPos; 164 } 165 166 /** 167 * 线程的构造方法 168 * 169 * @param threadName 170 * 线程的名字 171 * @param scrFile 172 * 源文件 173 * @param desPathAndName 174 * 目标文件的路径及其名称 175 * @param startPos 176 * 线程的开始位置 177 * @param endPost 178 * 线程的结束位置 179 */ 180 public CopyThread(String threadName, File srcFile, String desPathAndName, 181 long startPos, long endPos) { 182 super(threadName); 183 this.srcFile = srcFile; 184 this.desPath = desPath; 185 this.startPos = startPos; 186 this.endPost = endPos; 187 this.alreadyCopySize = this.startPos; 188 189 // System.out.println(this.getName() + "开始位置:" + startPos + " 结束位置:" 190 // + endPos); 191 192 // 初始化随机输入流,输出流 193 try { 194 rin = new RandomAccessFile(srcFile, "r"); 195 rout = new RandomAccessFile(desPathAndName, "rw"); 196 197 // 定位随机流的开始位置 198 rin.seek(startPos); 199 rout.seek(startPos); 200 201 // 开始线程 202 this.start(); 203 } catch (FileNotFoundException e) { 204 e.printStackTrace(); 205 } catch (IOException e) { 206 e.printStackTrace(); 207 } 208 } 209 210 public void run() { 211 int len = 0; 212 byte[] b = new byte[1024]; 213 214 try { 215 while ((alreadyCopySize < endPost) && (len = rin.read(b)) != -1) { 216 alreadyCopySize = alreadyCopySize + len; 217 if (alreadyCopySize >= this.endPost) { 218 int oldSize = (int) (alreadyCopySize - len); 219 len = (int) (this.endPost - oldSize); 220 alreadyCopySize = oldSize + len; 221 } 222 rout.write(b, 0, len); 223 } 224 System.out.println(this.getName() + " 在工作: 开始位置:" + this.startPos 225 + " 拷贝了:" + (this.endPost - this.startPos) + " 结束位置:" 226 + this.endPost); 227 228 } catch (IOException e) { 229 e.printStackTrace(); 230 } finally { 231 try { 232 if (rin != null) { 233 rin.close(); 234 } 235 } catch (IOException e) { 236 e.printStackTrace(); 237 } 238 239 try { 240 if (rout != null) { 241 rout.close(); 242 } 243 } catch (IOException e) { 244 // TODO Auto-generated catch block 245 e.printStackTrace(); 246 } 247 } 248 } 249} 250 251/** 252 * 由于Java的简单类型不能够精确的对浮点数进行运算, 253 * 这个工具类提供精 确的浮点数运算,包括加减乘除和四舍五入。 254 * @author haokui 255 * 256 */ 257class Arith { 258 // 默认除法运算精度 259 private static final int DEF_DIV_SCALE = 10; 260 261 // 这个类不能实例化 262 private Arith() { 263 } 264 265 /** 266 * 提供精确的加法运算。 267 * 268 * @param v1 269 * 被加数 270 * @param v2 271 * 加数 272 * @return 两个参数的和 273 */ 274 public static double add(double v1, double v2) { 275 BigDecimal b1 = new BigDecimal(Double.toString(v1)); 276 BigDecimal b2 = new BigDecimal(Double.toString(v2)); 277 return b1.add(b2).doubleValue(); 278 } 279 280 /** 281 * 提供精确的减法运算。 282 * 283 * @param v1 284 * 被减数 285 * @param v2 286 * 减数 287 * @return 两个参数的差 288 */ 289 public static double sub(double v1, double v2) { 290 BigDecimal b1 = new BigDecimal(Double.toString(v1)); 291 BigDecimal b2 = new BigDecimal(Double.toString(v2)); 292 return b1.subtract(b2).doubleValue(); 293 } 294 295 /** 296 * 提供精确的乘法运算。 297 * 298 * @param v1 299 * 被乘数 300 * @param v2 301 * 乘数 302 * @return 两个参数的积 303 */ 304 public static double mul(double v1, double v2) { 305 BigDecimal b1 = new BigDecimal(Double.toString(v1)); 306 BigDecimal b2 = new BigDecimal(Double.toString(v2)); 307 return b1.multiply(b2).doubleValue(); 308 } 309 310 /** 311 * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到 小数点以后10位,以后的数字四舍五入。 312 * 313 * @param v1 314 * 被除数 315 * @param v2 316 * 除数 317 * @return 两个参数的商 318 */ 319 public static double div(double v1, double v2) { 320 return div(v1, v2, DEF_DIV_SCALE); 321 } 322 323 /** 324 * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指 定精度,以后的数字四舍五入。 325 * 326 * @param v1 327 * 被除数 328 * @param v2 329 * 除数 330 * @param scale 331 * 表示表示需要精确到小数点以后几位。 332 * @return 两个参数的商 333 */ 334 public static double div(double v1, double v2, int scale) { 335 if (scale < 0) { 336 throw new IllegalArgumentException( 337 "The scale must be a positive integer or zero"); 338 } 339 BigDecimal b1 = new BigDecimal(Double.toString(v1)); 340 BigDecimal b2 = new BigDecimal(Double.toString(v2)); 341 return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue(); 342 } 343 344 /** 345 * 提供精确的小数位四舍五入处理。 346 * 347 * @param v 348 * 需要四舍五入的数字 349 * @param scale 350 * 小数点后保留几位 351 * @return 四舍五入后的结果 352 */ 353 public static double round(double v, int scale) { 354 if (scale < 0) { 355 throw new IllegalArgumentException( 356 "The scale must be a positive integer or zero"); 357 } 358 BigDecimal b = new BigDecimal(Double.toString(v)); 359 BigDecimal one = new BigDecimal("1"); 360 return b.divide(one, scale, BigDecimal.ROUND_HALF_UP).doubleValue(); 361 } 362};

假设复制e:/gcfr下的一个war包到e盘根目录下。运行结果如下:

 注意:10个线程同时工作,输出的顺序不一样正式体现。进度最后不是100%是因为统计的时候加了个延时,要看最后一个线程的结束位置,如果和文件的大小相等,表示就复制成功,没有字节丢失。此文件的大小是30995468

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

java 复制Map对象(深拷贝与浅拷贝)

java复制Map对象(深拷贝与浅拷贝)CreationTime2018年6月4日10点00分Author:Marydon1.深拷贝与浅拷贝  浅拷贝:只复制对象的引用,两个引用仍然指向同一个对象

java多线程实现复制功能并统计进度 - HelloWorld