package com; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * 多线程断点下砸 * *1. 通过网络访问获取文件大小, 然后调用RandomAccess生成相同大小的空文件(如果文件存在就不需要生成) * *2. 生成三个线程同时定义一个集合来保存下载标记 * *3. 当下载标记满足的时候,就表示加载完成 * * 多线程断点下载 同时支持多个任务执行 ,设计上还是有问题的 , 比如应该将任务集体封装, 其内部实现三个线程进行下载. */ public class MutilDownLoadDemo { private static volatile MutilDownLoadDemo demo; /** 默认的线程数, 一个任务分配三个线程来完成, 比如 两个任务就需要 3 * 2 = 6 个线程来执行 */ private static final int DEFAULT_THREAD_COUNT = 3; private int mThreadCount; private ThreadPoolExecutor mExecutor; public static MutilDownLoadDemo newInstance(){ if(demo == null){ synchronized (MutilDownLoadDemo.class) { if(demo == null){ demo = new MutilDownLoadDemo(DEFAULT_THREAD_COUNT); } } } return demo; } private MutilDownLoadDemo(int threadCount){ this.mThreadCount = threadCount; //初始化线程池 mExecutor = new ThreadPoolExecutor(mThreadCount, mThreadCount, 1, TimeUnit.MINUTES, new LinkedBlockingDeque<Runnable>()); } private synchronized void load(String path){ try { //1. 获取文件的大小 URL url = new URL(path); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setReadTimeout(5000); int code = conn.getResponseCode(); if(code == 200){ long contentLength = conn.getContentLength(); System.out.println("contentLength:" + contentLength); //2. 根据文件的大小来设置每个线程下砸的文件的数量 long block = contentLength / mThreadCount; long remain = contentLength % mThreadCount; System.out.println("block:" + block + " remain:" + remain); //3. 定义一个锁,让三个线程同时完成之后,才能执行之后的 CyclicBarrier barrier = new CyclicBarrier(mThreadCount, new ClearCacheThread(path)); //4. 开启线程进行下载 for(int i = 0 ; i < mThreadCount ; i ++){ long start = i * block; long end = (i + 1) * block - 1; if(i == mThreadCount -1){end = contentLength;} mExecutor.execute(new DownLoadThread(path, start, end, barrier, i)); } } } catch (IOException e) { e.printStackTrace(); } } /** * @param args */ public static void main(String[] args) { String url = "http://192.168.0.194:8080/a.zip"; MutilDownLoadDemo demo = MutilDownLoadDemo.newInstance(); demo.load(url); String url1 = "http://192.168.0.194:8080/b.zip"; demo.load(url1); } /** 用来下载的线程类 */ public class DownLoadThread extends Thread{ private String path; private long start; private long end; private CyclicBarrier mBarrier; private int tag; public DownLoadThread(String path ,long start, long end,CyclicBarrier barrier,int tag){ this.path = path; this.start = start; this.end = end; this.mBarrier = barrier; this.tag = tag; } @Override public void run() { try { //需要判断下载文件是否存在,如果存在,已经下载了多少 long loadLen = buildTempFile(path,tag); start += loadLen; URL url = new URL(path); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setConnectTimeout(5000); conn.setRequestProperty("Range", "bytes="+start+"-"+end); System.out.println(Thread.currentThread().getName() + "/start:" + start + " end:" + end); int code = conn.getResponseCode(); System.out.println(Thread.currentThread().getName() + "/code:" + code); if(code == 206){ //使用RandomAccessFile一个字节一个字节的写 RandomAccessFile raf = new RandomAccessFile(new File(getFilePath(path)), "rwd"); raf.setLength(conn.getContentLengthLong()); raf.seek(start); InputStream iStream = conn.getInputStream(); int len = -1; byte[] buf = new byte[1024]; while((len = iStream.read(buf)) != -1){ raf.write(buf, 0, len); loadLen += len; RandomAccessFile tempRaf = new RandomAccessFile(getFilePath(path) + "_" + tag + ".txt", "rwd"); tempRaf.write(String.valueOf(loadLen).getBytes()); System.out.println(Thread.currentThread().getName() + "/loadLen:" + loadLen); tempRaf.close(); } raf.close(); iStream.close(); conn.disconnect(); mBarrier.await(); System.out.println(Thread.currentThread().getName() + "/ await"); } } catch (MalformedURLException | InterruptedException | BrokenBarrierException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } private long buildTempFile(String path, int tag) { String filePath = getFilePath(path) + "_" + tag + ".txt"; System.out.println("filePath:" + filePath); File file = new File(filePath); if(file.exists()){ try { BufferedReader bufr = new BufferedReader(new FileReader(file)); String count = bufr.readLine(); bufr.close(); return Long.parseLong(count); } catch (IOException e) { e.printStackTrace(); } return 0; }else{ try { file.createNewFile(); BufferedWriter bufW = new BufferedWriter(new FileWriter(file)); bufW.write(String.valueOf(0)); bufW.close(); } catch (IOException e) { e.printStackTrace(); } return 0; } } } private String getFilePath(String path) { return "d:\\download\\" + path.substring(path.lastIndexOf("/") + 1); } /** 下载完成之后清除相应的缓存数据 */ public class ClearCacheThread extends Thread{ private String path; public ClearCacheThread(String path){ this.path = path; } @Override public void run() { System.out.println("下载完成-" + path); } } }