服务器之家

服务器之家 > 正文

java多线程下载实例详解

时间:2020-03-13 13:37     来源/作者:sgx425021234

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

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

多线程下载的实现过程:

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

?
1
2
3
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类指定每条线程从本地文件的什么位置开始写入数据

 

?
1
2
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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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程序设计有所帮助。

标签:

相关文章

热门资讯

玄元剑仙肉身有什么用 玄元剑仙肉身境界等级划分
玄元剑仙肉身有什么用 玄元剑仙肉身境界等级划分 2019-06-21
沙雕群名称大全2019精选 今年最火的微信群名沙雕有创意
沙雕群名称大全2019精选 今年最火的微信群名沙雕有创意 2019-07-07
男生常说24816是什么意思?女生说13579是什么意思?
男生常说24816是什么意思?女生说13579是什么意思? 2019-09-17
超A是什么意思 你好a表达的是什么
超A是什么意思 你好a表达的是什么 2019-06-06
华为nova5pro和p30pro哪个好 华为nova5pro和华为p30pro对比详情
华为nova5pro和p30pro哪个好 华为nova5pro和华为p30pro对比详情 2019-06-22
返回顶部