黑马程序员技术交流社区

标题: 自定义的多线程下载程序为什么有时候会下载不完! [打印本页]

作者: 张贺    时间: 2014-3-24 17:09
标题: 自定义的多线程下载程序为什么有时候会下载不完!
自己弄的个多线程下载的程序,为什么有时候可以下载完全,有时候会卡在一个地方不动?下载代码如下:
  1. import java.io.*;
  2. import java.net.*;

  3. /*
  4. 需求:自定义设计多线程下载工具类
  5. 思路:
  6. 1,先通过URL对象获取到需下载的内容的地址并连接到指定URL获取文件信息
  7. 2,计算所需下载文件的大小,并按照需要(线程数)将文件等分
  8. 3,创建多个线程,每个线程都会负责相应的下载部分(主要利用RandomAccessFile类的任意存储特点)
  9. 4,进行测试
  10. */
  11. public class DownLoad
  12. {
  13.         //定义下载资源的路径
  14.         private String path;
  15.         //指定下载的文件的保存位置
  16.         private String targetFile;
  17.         //定义需要使用多少个线程下载资源
  18.         private int threadNum;
  19.         //定义下载的线程对象
  20.         private DownThread[] threads;
  21.         //定义文件的总大小
  22.         private long fileSize;
  23.         //定义构造器,初始化资源路径,线程数等字段
  24.         public DownLoad(String path,String targetFile,int threadNum)
  25.         {
  26.                 this.path=path;
  27.                 this.targetFile=targetFile;
  28.                 this.threadNum=threadNum;
  29.                 //初始化trheads数组
  30.                 threads=new DownThread[threadNum];
  31.         }
  32.         //定义方法,用以获取下载文件的相关信息,并根据线程数将文件进行等分
  33.         public void download()throws Exception
  34.         {
  35.                 //定义并初始化指定路径的URL对象
  36.                 URL url=new URL(path);
  37.                 //通过URL对象获取对指定路径的连接
  38.                 HttpURLConnection conn=(HttpURLConnection)url.openConnection();
  39.                 //设置连接超时属性
  40.                 conn.setConnectTimeout(5*1000);
  41.                 //设置连接请求方式
  42.                 conn.setRequestMethod("GET");
  43.                 //设置请求的接收文件的相关属性
  44.                 conn.setRequestProperty("Accept","*/*");
  45.                 conn.setRequestProperty("Accept-Language","zh-CN");
  46.                 conn.setRequestProperty("Charset","UTF-8");
  47.                 conn.setRequestProperty("Connection","Keep-Alive");
  48.                 //得到文件的大小
  49.                 fileSize=conn.getContentLength();
  50.                 conn.disconnect();
  51.                 //按线程数将文件等分
  52.                 long partSize=fileSize/threadNum+1;
  53.                 //使用指定文件创建RandomAccessFile对象
  54.                 RandomAccessFile file=new RandomAccessFile(targetFile,"rw");
  55.                 //设置本地文件的大小
  56.                 file.setLength(fileSize);
  57.                 file.close();
  58.                 //创建并启动线程
  59.                 for(int i=0;i<threadNum;i++)
  60.                 {
  61.                         //计算每个线程下载的开始位置
  62.                         long startPos=i*partSize;
  63.                         //每个线程使用一个RandomAccessFile进行下载
  64.                         RandomAccessFile part=new RandomAccessFile(targetFile,"rw");
  65.                         //设置下载位置
  66.                         part.seek(startPos);
  67.                         //创建下载线程
  68.                         threads[i]=new DownThread(startPos,partSize,part);
  69.                         //启动线程
  70.                         threads[i].start();
  71.                 }
  72.         }
  73.         //定义方法用于获取下载完成的百分比
  74.         public double getComplete()
  75.         {
  76.                 //统计所有线程已经下载的大小的总和
  77.                 int sumSize=0;
  78.                 for(int i=0;i<threadNum;i++)
  79.                 {
  80.                         sumSize+=threads[i].length;
  81.                 }
  82.                 //返回已完成的百分比
  83.                 return sumSize*1.0/fileSize;
  84.         }
  85.         //测试代码
  86.         public String getCompletes()
  87.         {
  88.                 //统计所有线程已经下载的大小的总和
  89.                 int sumSize=0;
  90.                 for(int i=0;i<threadNum;i++)
  91.                 {
  92.                         sumSize+=threads[i].length;
  93.                 }
  94.                 //返回已完成的百分比
  95.                 return fileSize+" "+threads[0].length+" "+threads[1].length+" "+threads[2].length+"

  96. "+threads[3].length+" "+(sumSize*1.0/fileSize);
  97.         }
  98.         //定义内部线程类,用于下载文件
  99.         private class DownThread extends Thread
  100.         {
  101.                 //当前线程的下载位置
  102.                 private long startPos;
  103.                 //定义当前线程负责下载的文件大小
  104.                 private long partSize;
  105.                 //定义下载用的文件对象
  106.                 private RandomAccessFile part;
  107.                 //计算已经下载的文件大小
  108.                 public long length=0;
  109.                 //定义构造器,初始化数据
  110.                 public DownThread(long startPos,long partSize,RandomAccessFile part)
  111.                 {
  112.                         this.startPos=startPos;
  113.                         this.partSize=partSize;
  114.                         this.part=part;
  115.                 }
  116.                 //重写run方法
  117.                 public void run()
  118.                 {
  119.                         try
  120.                         {
  121.                                 //定义并初始化指定路径的URL对象
  122.                                 URL url=new URL(path);
  123.                                 //通过URL对象获取对指定路径的连接
  124.                                 HttpURLConnection conn=(HttpURLConnection)url.openConnection();
  125.                                 //设置连接超时属性
  126.                                 conn.setConnectTimeout(5*1000);
  127.                                 //设置连接请求方式
  128.                                 conn.setRequestMethod("GET");
  129.                                 //设置请求的接收文件的相关属性
  130.                                 conn.setRequestProperty("Accept","*/*");
  131.                                 conn.setRequestProperty("Accept-Language","zh-CN");
  132.                                 conn.setRequestProperty("Charset","UTF-8");
  133.                                 //获取文件输入流
  134.                                 InputStream inStream=conn.getInputStream();
  135.                                 //跳过startPos个字节(只下载自己负责的部分)
  136.                                 inStream.skip(startPos);
  137.                                 //定义字节数组,保存数据
  138.                                 byte[] buf=new byte[1024];
  139.                                 int hasRead=0;
  140.                                 //读取网络数据,并写入本地文件
  141.                                 while(length<partSize&&(hasRead=inStream.read(buf))!=-1)
  142.                                 {
  143.                                         //将数据写入指定文件的指定位置
  144.                                         part.write(buf,0,hasRead);
  145.                                         //累计下载的总大小
  146.                                         length+=hasRead;
  147.                                         this.yield();
  148.                                 }
  149.                                 part.close();
  150.                                 inStream.close();
  151.                         }
  152.                         catch(Exception e)
  153.                         {
  154.                                 e.printStackTrace();
  155.                         }
  156.                 }
  157.         }
  158. }
复制代码
测试代码如下:
  1. import java.lang.Math;
  2. /*
  3. 测试程序
  4. */

  5. public class DownTest
  6. {
  7.         public static void main(String[] args)throws Exception
  8.         {       
  9.                 //初始化DownLoad对象
  10.                 final DownLoad download=new DownLoad

  11. ("http://zhangmenshiting.baidu.com/data2/music/57130533/73007331395604861128.mp3?

  12. xcode=b453c78019aaef462150ccef90c23ba01489e1db000d3934","测试.mp3",4);
  13.                 //开始下载
  14.                 download.download();
  15.                 //定义线程,每过0.1秒统计任务的完成度
  16.                 new Thread()
  17.                 {
  18.                         public void run()
  19.                         {
  20.                                 while(download.getComplete()<1)
  21.                                 {
  22.                                         System.out.println("已完成:"+download.getComplete());
  23.                                         try
  24.                                         {
  25.                                                 Thread.sleep(1000);
  26.                                         }
  27.                                         catch(Exception e){}
  28.                                 }
  29.                         }
  30.                 }.start();
  31.         }
  32. }
复制代码









欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2