多线程下载Java实现

一、多线程加速下载下载

1.不是说线程开的越多下载就越快  例:手机迅雷(建议3-4个线程)

2.还受服务器带宽的影响

3.相当于更多的cpu资源给了你

二、多线程下载步骤分析

1.获取文件大小

2.在客户端创建一个大小和服务器一模一样的文件,提前申请好空间

3.每个线程下载的开始位置和结束位置

4.开多个线程去下载文件

4.知道每个线程什么时候下载完毕了

1.获取文件的大小

public static void main(String[] args) {
	
	//【一★★★★★★★】获取服务器文件大小
	new Thread() {
		@Override
		public void run() {
			try {
				//【2】定义下载路径
				String path = "http://192.168.3.138:8080/ttt.exe";
				//【2.2】创建URL对象指定我们要访问的网址(路径)
				URL url = new URL(path);
				//【2.3】拿到HttpURLConnection对象 用于发送或接受数据
				HttpURLConnection conn = (HttpURLConnection) url.openConnection();
				//【2.4】设置发送get请求
				conn.setRequestMethod("GET");
				//【2.5】设置请求时间
				conn.setConnectTimeout(5000);
				//【2.6】获取服务器返回的状态码
				int code = conn.getResponseCode();
				//【2.7】如果code == 200说明请求成功
				if (code == 200) {
					//★★★★★获取服务器文件大小
					int length = conn.getContentLength();
					System.out.println("length:"+length);
					
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}.start();
}

2.每个线程下载的计算公式

 

3.实现代码

package down;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;

public class MutioDown {

	// 【1】定义下载路径
	static String path = "http://192.168.3.138:8080/ttt.exe";
	private static final int threadCount = 3;// 假设开三个线程
	private static int runningThread;

	public static void main(String[] args) {

		try {
			// 【2】创建URL对象指定我们要访问的网址(路径)
			URL url = new URL(path);
			// 【3】拿到HttpURLConnection对象 用于发送或接受数据
			HttpURLConnection conn = (HttpURLConnection) url.openConnection();
			// 【4】设置发送get请求
			conn.setRequestMethod("GET");
			// 【5】设置请求时间
			conn.setConnectTimeout(5000);
			// 【6】获取服务器返回的状态码
			int code = conn.getResponseCode();
			// 【6.1】如果code == 200说明请求成功
			if (code == 200) {
				// 【7】获取服务器文件大小
				int length = conn.getContentLength();
				runningThread = threadCount;
				System.out.println("length:" + length);

				// 【二★★★★★★★】创建一个大小和服务器一模一样的文件,目的提前把空间申请出来
				RandomAccessFile rafAccessFile = new RandomAccessFile(
						getFilename(path), "rw");
				rafAccessFile.setLength(length);

				// 【7】计算出每个线程下载的大小
				int blockSize = length / threadCount;

				// 【三★★★★★★★】计算每个线程下载的开始位置和结束位置
				for (int i = 0; i < threadCount; i++) {
					int starIndex = i * blockSize; // 每个线程下载的开始位置
					int endIndex = (i + 1) * blockSize - 1;// 每个线程下载的结束位置
					// 特殊情况 就是最后一个线程
					if (i == threadCount - 1) {
						endIndex = length - 1;
					}

					// 【四★★★★★★★】开启线程去服务器下载文件
					new DownLoadThread(starIndex, endIndex, i).start();

				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private static class DownLoadThread extends Thread {

		// 通过构造方法把每个线程下载的开始位置和结束位置传递进来
		private int starIndex;
		private int endIndex;
		private int threadId;

		public DownLoadThread(int starIndex, int endIndex, int threadId) {
			this.starIndex = starIndex;
			this.endIndex = endIndex;
			this.threadId = threadId;
		}

		@Override
		public void run() {
			// 【四★★★★★★】实现去服务器下载的逻辑
			try {
				URL url = new URL(path);
				HttpURLConnection conn = (HttpURLConnection) url
						.openConnection();
				conn.setRequestMethod("GET");
				conn.setConnectTimeout(5000);

				// 如果中间断过,继续在上次的位置继续下载,从文件中读取上次下载的位置
				File file = new File(getFilename(path) + threadId + ".txt");
				if (file.exists() && file.length() > 0) {
					FileInputStream fis = new FileInputStream(file);
					BufferedReader bufr = new BufferedReader(
							new InputStreamReader(fis));
					String lastPositionn = bufr.readLine(); // 读取出来的内容就是上次下载的位置
					int lastPosition = Integer.parseInt(lastPositionn);

					// 要改变一下startIndex的位置
					starIndex = lastPosition;
					fis.close();
				}

				// ★★★★★设置一个请求头Range(作用告诉服务器每个线程下载的开始位置和结束位置)
				conn.setRequestProperty("Range", "bytes=" + starIndex + "-"
						+ endIndex);

				int code = conn.getResponseCode();
				if (code == 206) { // 200表示获取服务器资源全部成功,206表示请求部分资源成功

					// 创建随机读写文件对象
					RandomAccessFile raf = new RandomAccessFile(
							getFilename(path), "rw");
					// 每个线程要从自己的位置开始写
					raf.seek(starIndex);

					InputStream in = conn.getInputStream(); // 存的是ttt.exe

					// 把数据写到文件中
					int len = -1;
					byte[] buffer = new byte[1024 * 1024];

					int total = 0; // 代表当前线程下载的大小

					while ((len = in.read(buffer)) != -1) {
						raf.write(buffer, 0, len);

						// 实现断点续传 就是把当前线程下载的位置存起来 下次再下载的时候按上次下载的位置继续下载就可以了
						total += len;
						int currentThreadPosition = starIndex = total;
						RandomAccessFile raff = new RandomAccessFile(
								getFilename(path) + threadId + ".txt", "rwd");
						raff.write(String.valueOf(currentThreadPosition)
								.getBytes());
						raff.close();
					}
					raf.close();// 释放资源

					System.out.println("线程:" + threadId + "--下载完了");
					synchronized (DownLoadThread.class) {
						runningThread--;
						if (runningThread == 0) { // 说明所有线程都执行完毕了
							for (int i = 0; i < threadCount; i++) {
								File deleFile = new File(getFilename(path) + i
										+ ".txt");
								deleFile.delete();
							}
						}
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	public static String getFilename(String path) { // 获取文件的名字

		int start = path.lastIndexOf("/") + 1;
		return path.substring(start);
	}

}

 

1.得到服务器下载文件的大小,然后在本地设置一个临时文件和服务器端文件大小一致 a)获得访问网络地址 b)通过URL对象的openConnection()方法打开连接,返回一个连接对象 c)设置请求头 i.setRequestMethod ii.setConnectTimeout iii.setReadTimeout d)判断是否响应成功 e)获取文件长度(getContentLength()) f)随机访问文件的读取与写入RandomAccessFile(file, mode) g)设置临时文件与服务器文件大小一致(setLength()) h)关闭临时文件 2.计算出每个线程下载的大小(开始位置,结束位置) a)计算出每个线程下载的大小 b)for循环,计算出每个线程的开始、结束位置 c)最后一个线程处理 3.每创建好一次就要开启线程下载 a)构造方法 b)通过URL对象的openConnection()方法打开连接,返回一个连接对象 c)设置请求头 i.setRequestMethod ii.setConnectTimeout d)判断是否响应成功(206) e)获取每个线程返回的流对象 f)随机访问文件的读取与写入RandomAccessFile(file, mode) g)指定开始位置 h)循环读取 i.保存每个线程下载位置 ii.记录每次下载位置 iii.关闭临时记录位置文件 iv.随机本地文件写入 v.记录已下载大小 i)关闭临时文件 j)关闭输入流 4.为了杀死线程还能继续下载的情况下,从本地文件上读取已经下载文件的开始位置 a)创建保存记录结束位置的文件 b)读取文件 c)将流转换为字符 d)获取记录位置 e)把记录位置赋给开始位置 5.当你的n个线程都下载完毕的时候我进行删除记录下载位置的缓存文件 a)线程下载完就减去 b)当没有正在运行的线程时切文件存在时删除文件
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值