java多线程-下载文件及断点续传(番外篇)

java多线程-下载文件及断点续传(番外篇)

最主要的还是了解到了,网络资源可以分段下载:
conn.setRequestProperty(“Range”,”bytes=”+startIndex+”-“+endIndex);//设置请求资源大小
int code=conn.getResponseCode();
if(code==206){//从服务器请求全部资源200,请求部分资源为206.上面setRequestProperty设置了请求部分资源的大小了

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

public class ThreadUtil {
    private  int threadCount=3;//开启线程个数
    private int jsCount=3;//计数器
    public  void moreThreadLoad(String path){
        URL url;
        try {
            url = new URL(path);
            HttpURLConnection conn=(HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            conn.setConnectTimeout(5000);
            int code=conn.getResponseCode();
            if(code==200){
                int length=conn.getContentLength();//获取文件的大小
                //新建一个临时文件
                RandomAccessFile ref= new RandomAccessFile("1.mov", "rwd");//写入磁盘
                ref.setLength(length);//临时文件大小
                ref.close();
                int blockSize=length/threadCount;//平均分配每个线程大小
                for(int i=1;i<=threadCount;i++){
                    int startIndex=(i-1)*blockSize;
                    int endIndex=i*blockSize-1;
                    if(i==threadCount){
                        endIndex=length;//最后一个线程承接后面所有的大小
                    }
                    System.out.println("线程:"+i+"下载----"+startIndex+"----&gt;"+endIndex);
                    new MuilThread(path,i,startIndex,endIndex).start();
                }
            }

        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

    private class MuilThread extends Thread{
        private int index;
        private int startIndex;
        private int endIndex;
        private String path;
        public MuilThread(String path,int index,int startIndex,int endIndex){
            this.path=path;
            this.index=index;
            this.startIndex=startIndex;
            this.endIndex=endIndex;
        }
        @Override
        public void run() {
            try{
                //读取文件
                File tempFile=new File(index+".txt");
                if(tempFile.exists()&&tempFile.length()>0){
                    FileInputStream input=new FileInputStream(tempFile);
                    byte[] temp=new byte[1024];
                    int leng=input.read(temp);
                    String downloadlen=new String(temp,0,leng);
                    int downIndex=Integer.parseInt(downloadlen);
                    startIndex=downIndex;
                    input.close();
                }

                URL url = new URL(path);
                HttpURLConnection conn=(HttpURLConnection) url.openConnection();
                conn.setRequestMethod("GET");
                conn.setRequestProperty("Range","bytes="+startIndex+"-"+endIndex);//设置请求资源大小
                conn.setConnectTimeout(5000);
                int code=conn.getResponseCode();
                if(code==206){//从服务器请求全部资源200,请求部分资源为206.上面setRequestProperty设置了请求部分资源的大小了
                    InputStream is=conn.getInputStream();
                    RandomAccessFile ref= new RandomAccessFile("1.mov", "rwd");//写入磁盘
                    //指定开始写入文件的起始位置
                    System.out.println("startIndex=="+startIndex);
                    ref.seek(startIndex);//定位文件
                    int len=0;
                    byte[] buffer=new byte[1024];
                    int total=0;

                    while((len=is.read(buffer))!=-1){
                        RandomAccessFile info=new RandomAccessFile(index+".txt", "rwd");
                        ref.write(buffer,0,len);
                        total+=len;
                        info.write(String.valueOf(total).getBytes());
                        System.out.println("线程"+index+"--"+total);
                        info.close();
                    }
                    is.close();
                    ref.close();
                    System.out.println("线程:"+index+"下载完毕");
                }else{
                    System.out.println("线程:"+index+"下载失败");
                }
            }catch(Exception ex){
                ex.printStackTrace();
            }finally{
                jsCount--;
                if(jsCount==0){//所有线程已经下载完毕则删除文件
                    for(int i=1;i<=3;i++){
                        File dfile=new File(i+".txt");
                        if(dfile.exists()){
                            dfile.delete();
                        }
                    }
                }
            }
        }
    }

    public static void  main(String org[]){
        new ThreadUtil().moreThreadLoad("http://localhost:8089/1.mov");
    }
}

上面的代码明显有个地方是线程不安全的;jsCount–。
jsCount是一个公共成员变量,new MuilThread(path,i,startIndex,endIndex).start();new了三个线程出来,而它们三个变量却共同操作jsCount变量。当时做多线程下载文件时,确实可以下载完成没问题因为在下载过程中并没有使用到jsCount变量,所以很正常,但是这里还是要说明一下jsCount变量不是安全的问题。

package cn.thread.first.syn;

public class ThreadUtil {

    private int jsCount = 3;//计数器


    public void moreThreadLoad(String path) {
        Object object = new Object();
        for (int i = 0; i < 3; i++) {
            System.out.println("线程:" + i + "下载----");
            new MuilThread(object).start();
        }

    }


    private class MuilThread extends Thread {

        private final Object object;

        public MuilThread(Object object) {
            this.object = object;
        }

        @Override
        public void run() {
//            synchronized (object) {
//
//            }
            try {
                Thread.sleep(2000);
            } catch (Exception ex) {
                ex.printStackTrace();
            } finally {
                jsCount--;
                System.out.println("jscount=" + jsCount);
                if(jsCount==0){
                    System.out.println("jscount=END" + jsCount);
                }
            }
        }
    }

    public static void main(String org[]) {
        new ThreadUtil().moreThreadLoad("http://localhost:8089/1.mov");
    }
}

输出结果:
线程:0下载—-
线程:1下载—-
线程:2下载—-
jscount=1
jscount=0
jscount=END0
jscount=0
jscount=END0
jscount=END0
或者是:
线程:0下载—-
线程:1下载—-
线程:2下载—-
jscount=1
jscount=1
jscount=1

还有很多中结果,
一次都没有输出jscount=END0,或者输出多次jscount=END0,也有正确输出一次jscount=END0的。为什么?
因为多个线程同时去操作jsCount变量,它的值无法确定。 如果改进呢?

package cn.thread.first.syn;

import java.util.Date;

public class ThreadUtil {

    private int jsCount = 3;//计数器


    public void moreThreadLoad(String path) {
        Object object = new Object();//创建一个对象,下面多线程里面当锁使用
        for (int i = 0; i < 3; i++) {
            System.out.println("线程:" + i + "下载----");
            new MuilThread(object).start();
        }

    }


    private class MuilThread extends Thread {

        private final Object object;

        public MuilThread(Object object) {
            this.object = object;
        }

        @Override
        public void run() {
            try {
                Thread.sleep(2000);
            } catch (Exception ex) {
                ex.printStackTrace();
            } finally {
               //多线程时,它们使用的是同一个锁对象,所以这里将同步进行
                synchronized (object) {
                    jsCount--;
                    System.out.println("jscount=" + jsCount);
                    if(jsCount==0){
                        System.out.println("jscount=END" + jsCount);
                    }
                }

            }

        }
    }

    public static void main(String org[]) {
        new ThreadUtil().moreThreadLoad("http://localhost:8089/1.mov");
    }
}

输出结果:
线程:0下载—-
线程:1下载—-
线程:2下载—-
jscount=2
jscount=1
jscount=0
jscount=END0

解说:new MuilThread(object).start();每个线程里面都带有了object这个对象,而jsCount–时使用了object这个对象锁,所以它们之间互斥同步了。
那也许会问,使用了同步之后,线程1,线程2,线程3就变成顺序执行的了,那么是不是还不如不顺序执行,何必使用多线程呢?
1.启动多线程时,可以充分利用多核CPU,比如run方法里面它们同时下载文件时,可以三个线程一起下载,避免了io,CPU的浪费,它们只有到了结束的时候jsCount–的时候才排队进行。现在是三个人一起干活,活干完了然后排队出去而已;如果换成但线程的就变成一个人进来干活,然后出去,下一个人进来干活,出去,谁的效率高。
2.多线程在互斥时,线程阻塞有时候却是还不如单线程,但是这里的场景只是jsCount–,所以多线程比单线程效率高很多。

猜你喜欢

转载自blog.csdn.net/piaoslowly/article/details/81476086
今日推荐