多线程下载网络资源

创建多个线程在单位时间内便可以获得更多的服务端资源,可以加快下载速度。但是也并不是线程越多越好,假设我们在客户端新建了10000个线程,此时服务端的CPU就会在线程间的轮询切换上消耗大量的时间,而真正用来下载数据资源的时间就会减少。同时,由于客户端和服务端物理带宽的限制,下载速度也会受到极大的关联。

下面我将用多线程下载网络上的资源,代码如下:


package hxl.insist.two;

import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.CountDownLatch;

/**
 * 多线程从网络服务器上下载资源
 * @author Hanxl
 */
public class MultiThreadDownload {
	/**
	 * 下载的线程数量
	 */
	private int threadCount;
	
	/**
	 * 资源的Url路径
	 */
	private String resourcePath;
	
	/**
	 * 保存到本地的文件名
	 */
	private String fileName;
	
	/**
	 * 锁存器,判断所有子线程是否运行完毕
	 */
	final CountDownLatch cdl = new CountDownLatch(threadCount);
	
	public MultiThreadDownload(int threadCount,String resourcePath,String fileName) {
		this.threadCount = threadCount;
		this.resourcePath = resourcePath;
		this.fileName = fileName;
	}
	
	/**
	 * 在本地创建一个和网络资源相同大小的文件
	 * @return 网络资源文件大小
	 */
	private int createLocalFile() throws Exception {
		URL url = new URL(resourcePath);
		HttpURLConnection conn = (HttpURLConnection) url.openConnection();
		//将请求方式设为Get
		conn.setRequestMethod("GET");
		//设置请求超时时间
		conn.setConnectTimeout(5000);
		//获取响应的状态码
		int statusCode = conn.getResponseCode();
		int resourceSize = 0;
		if(statusCode == 200) {
			//取得网络资源文件的大小
			resourceSize = conn.getContentLength();
			//在本地创建一个和网络资源相同大小的文件
			RandomAccessFile localFile = new RandomAccessFile(fileName, "rw");
			localFile.setLength(resourceSize);
		}
		return resourceSize;
	}
	
	/**
	 *	创建线程并从网络上开始下载资源
	 */
	private void createThreadAndDownload(final int startIndex,final int endIndex) {
		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					URL url = new URL(resourcePath);
					HttpURLConnection conn = (HttpURLConnection) url.openConnection();
					conn.setRequestMethod("GET");
					conn.setConnectTimeout(5000);
					//为服务器指定下载的资源是哪一部分
					conn.setRequestProperty("Range", "bytes=" + startIndex + "-"+endIndex);
					//206表示请求部分数据成功
					int code = conn.getResponseCode();
					if(code == 206) {
						RandomAccessFile localFile = new RandomAccessFile(fileName, "rw");
						InputStream is = conn.getInputStream();
						localFile.seek(startIndex);
						System.out.println(Thread.currentThread().getName() + " : " + "开始下载" + startIndex + "---" + endIndex);
						int len = -1;
						byte[] bufferArea = new byte[1024];
						while((len = is.read(bufferArea)) != -1) {
							localFile.write(bufferArea, 0, len);
						}
						is.close();
						localFile.close();
						//递减锁存器的计数,如果计数到达零,则释放所有等待的线程。
						cdl.countDown();
						System.out.println(Thread.currentThread().getName() + " : " + "下载完毕");
					}
				} catch (Exception e) {
					e.printStackTrace();
				} finally {
				}
			}
		}).start();
	}

	/**
	 * 让程序开始从网络上下载资源
	 */
	public void startDownload() throws Exception {
		int resourceSize = createLocalFile();
		if(resourceSize != 0) {
			//将资源大小按线程数量等分
			int resourceAverageSize = resourceSize / threadCount;
			for(int threadIndex = 0;threadIndex < threadCount;threadIndex++) {
				//为每个线程指定下载位置
				final int startIndex = threadIndex * resourceAverageSize;
				int endIndex = startIndex + resourceAverageSize - 1;
				if(threadIndex == (threadCount - 1)) {
					endIndex = resourceSize - 1;
				}
				createThreadAndDownload(startIndex,endIndex);
			}
		}
		
		while (true) {
			//直到锁存器倒计数至零之前一直等待
			cdl.await();
			break;
		}
	}
	
	public static void main(String[] args) {
		try {
			if(args.length == 3) {
				MultiThreadDownload mtd = new MultiThreadDownload(Integer.parseInt(args[0]),args[1],args[2]);
				mtd.startDownload();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

如果在有足够带宽的情况下,适当增加线程的个数,确实是加快了下载的速度。

你可能感兴趣的:(多线程下载网络资源)