软件编程
位置:首页>> 软件编程>> java编程>> java多线程下载实例详解

java多线程下载实例详解

作者:sgx425021234  发布时间:2023-05-19 05:53:37 

标签:java,多线程,下载

本文实例讲述了java多线程下载。分享给大家供大家参考,具体如下:

使用多线程下载文件可以更快完成文件的下载,多线程下载文件之所以快,是因为其抢占的服务器资源多。如:假设服务器同时最多服务100个用户,在服务器中一条线程对应一个用户,100条线程在计算机中并非并发执行,而是由CPU划分时间片轮流执行,如果A应用使用了99条线程下载文件,那么相当于占用了99个用户的资源,假设一秒内CPU分配给每条线程的平均执行时间是10ms,A应用在服务器中一秒内就得到了990ms的执行时间,而其他应用在一秒内只有10ms的执行时间。就如同一个水龙头,每秒出水量相等的情况下,放990毫秒的水肯定比放10毫秒的水要多。

多线程下载的实现过程:

1.首先得到下载文件的长度,然后设置本地文件的长度。


HttpURLConnection.getContentLength();
RandomAccessFile file = new RandomAccessFile("youdao.exe","rw");
file.setLength(filesize);//设置本地文件的长度

2.根据文件长度和线程数计算每条线程下载的数据长度和下载位置。如:文件的长度为6M,线程数为3,那么,每条线程下载的数据长度为2M,每条线程开始下载的位置如下图所示。

java多线程下载实例详解

3.使用Http的Range头字段指定每条线程从文件的什么位置开始下载,如:指定从文件的2M位置开始下载文件,代码如下:

HttpURLConnection.setRequestProperty("Range", "bytes=2097152-");


4.保存文件,使用RandomAccessFile类指定每条线程从本地文件的什么位置开始写入数据


RandomAccessFile threadfile = new RandomAccessFile("<span style="font-family: Arial, Helvetica, sans-serif;">youdao.exe</span><span style="font-family: Arial, Helvetica, sans-serif;"> ","rw");</span>
threadfile.seek(2097152);//从文件的什么位置开始写入数据

下面是通过具体实现类:

在写实现类之前我们首先要将要下载的文件放在服务器上并部署:

我是放在了这里 D:\Tomcat\apache-tomcat-7.0.37\webapps\doudou目录下,并启动D:\Tomcat\apache-tomcat-7.0.37\bin下的startup.bat

1.DownLoadTest.java


package www.csdn.net.down;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
public class DownLoadTest {
public File file;
public RandomAccessFile accessFile;
// 线程的数量
public static int threadNum = 3;
// 每个线程负责下载的大小
int blockSize;
// 创建访问的路径
public String path = "http://localhost:8080/doudou/youdao.exe";
public static int threadCount;// 数量
public void testDown() {
 try {
  // 创建出URL对象
  URL url = new URL(path);
  // 创建出 HttpURLConnection对象
  HttpURLConnection httpURLConnection = (HttpURLConnection) url
    .openConnection();
  // 设置 发请求发送的方式
  httpURLConnection.setRequestMethod("GET");
  // 设置请求是否超时时间
  httpURLConnection.setConnectTimeout(5000);
  // 设置
  httpURLConnection
    .setRequestProperty("User-Agent",
      " Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Trident/6.0)");
  // 是否响应成功
  if (httpURLConnection.getResponseCode() == 200) {
   // 获取文件的大小
   int size = httpURLConnection.getContentLength();
   System.out.println("文件的大小" + size);
   // 创建文件
   file = new File("youdao.exe");
   accessFile = new RandomAccessFile(file, "rwd");
   // 设置文件的大小
   accessFile.setLength(size);
   // 每个线程下载的大小
   blockSize = size / threadNum;
   // 开三个线程 操作此文件
   for (int i = 1; i <= threadNum; i++) {
    // 1 2 3
    // 计算出每个线程开始的位置
    int startSize = (i - 1) * blockSize;
    // 结束位置
    int endSize = (i) * blockSize;
    // 当线程是最后一个线程的时候
    if (i == threadNum) {
     // 判断文件的大小是否大于计算出来的结束位置
     if (size > endSize) {
      // 结束位置 等于 文件的大小
      endSize = size;
     }
    }
    // 为每个线程创建一个随机的读取
    RandomAccessFile threadAccessFile = new RandomAccessFile(
      file, "rwd");
    new Thread(new DownLoadThread(i, threadAccessFile,
      startSize, endSize, path)).start();
   }
  }
 } catch (MalformedURLException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
 } catch (IOException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
 }
}
public static void main(String[] args) {
 DownLoadTest downLoadTest = new DownLoadTest();
 // 调用下载方法
 downLoadTest.testDown();
}
}
class DownLoadThread implements Runnable {
// 下载文件的封装
public RandomAccessFile accessFile; // 每个线程 都拥有一个accessFile的文件对象 线程1 线程2 线程3
// 线程下载文件的起始位置
public int startSize;
public int endSize;
// 文件下载的path路径
public String path;
public int threadId; // 线程的标识
public DownLoadThread(int threadId, RandomAccessFile accessFile,
  int startSize, int endSize, String path) {
 this.threadId = threadId;
 this.accessFile = accessFile;
 this.startSize = startSize;
 this.endSize = endSize;
 this.path = path;
}
@Override
public void run() {
 // 执行run方法
 try {
  // 创建文件
  File threadFile = new File(threadId + ".txt");
  if (threadFile.exists()) {
   // 读取该文件的内容
   // 创建文件的输入流对象
   FileInputStream fis = new FileInputStream(threadFile);
   // 采用工具类读取
   byte data[] = StreamTools.isToData(fis);
   // 转化成字符串
   String threadLen = new String(data);
   if ((threadLen != null) && (!"".equals(threadLen))) {
    startSize = Integer.valueOf(threadLen);
    // 解决 416bug的错误
    if (startSize > endSize) {
     startSize = endSize - 1;
    }
   }
  }
  // 创建URL对象
  URL url = new URL(path);
  // 创建HttpURLConnection对象
  HttpURLConnection httpURLConnection = (HttpURLConnection) url
    .openConnection();
  // 设置请求的头
  httpURLConnection.setRequestMethod("GET");
  // 设置请求是否超时时间
  httpURLConnection.setConnectTimeout(5000);
  // 设置
  httpURLConnection
    .setRequestProperty("User-Agent",
      " Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Trident/6.0)");
  // 关键的设置
  httpURLConnection.setRequestProperty("Range", "bytes=" + startSize
    + "-" + endSize);
  // 输出当前线程
  System.out.println("当前线程" + threadId + " 下载开始位置:" + startSize
    + " 下载结束位置:" + endSize);
  // 响应成功
  // 设置随机读取文件的 开始位置
  accessFile.seek(startSize);
  // 获取相应流对象
  InputStream is = httpURLConnection.getInputStream();
  // 创建输出流对象
  byte buffer[] = new byte[1024];
  int len = 0;
  int threadTotal = 0;// 每个线程下载后保存记录 /
  while ((len = is.read(buffer)) != -1) {
   accessFile.write(buffer, 0, len);
   threadTotal += len;// 记录你写入的长度 //xml文件
   // 通过文件记录文件下载的长度
   FileOutputStream fos = new FileOutputStream(threadFile);
   fos.write((threadTotal + "").getBytes());
   fos.flush();
   fos.close();
  }
  accessFile.close();
  is.close();
  System.out.println(threadId + "线程执行完毕");
  //线程操作
  synchronized (DownLoadTest.class) {
   DownLoadTest.threadCount++;
   if (DownLoadTest.threadCount >= DownLoadTest.threadNum) {
    for(int i=1;i<=DownLoadTest.threadNum;i++){
     File file = new File(i+".txt");
     if(file.exists()){
      file.delete();
     }
    }
   }
  }
 } catch (MalformedURLException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
 } catch (IOException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
 }
}
}

2.流工具的封装 StreamTools.java


package www.csdn.net.down;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
public class StreamTools {
public static byte[] isToData(InputStream is) throws IOException{
 // 字节输出流
 ByteArrayOutputStream bops = new ByteArrayOutputStream();
 // 读取数据的缓存区
 byte buffer[] = new byte[1024];
 // 读取长度的记录
 int len = 0;
 // 循环读取
 while ((len = is.read(buffer)) != -1) {
  bops.write(buffer, 0, len);
 }
 // 把读取的内容转换成byte数组
 byte data[] = bops.toByteArray();
 bops.flush();
 bops.close();
 is.close();
 return data;
}
}

希望本文所述对大家Java程序设计有所帮助。

0
投稿

猜你喜欢

手机版 软件编程 asp之家 www.aspxhome.com