java 等待文件下载_多个文件开启固定线程等待下载--java实现

今天群友的一个需求,无聊中,所以写了个小的demo。

需求:要下载多个文件,但同时下载的文件最多为3个,如果超过则显示“等待下载”,等有文件下载完成后再从“等待下载”中开始下载,这样的下载应该在很多地方见过。

思路:这里不考虑多线程下载单个文件的情况,一切从简出发。先想到的是利用线程池(池A)把所有要下载的文件封装成一个线程放在这里做为资源,再有一个线程池(池B)用来存放正在下载的线程,池B类似生产者消费者模型。下载完一个后就从池B中移除,再从池A中添加一个进来。直到池A的线程用完。

需要考虑的是怎么知道什么时候池B要移除,池A要添加进去呢?且看代码:

下载的线程DownloadThread.java

public DownloadThread(String url, String toPath) {

try {

this.url = url;

this.file = new File(toPath);

}catch (Exception e){

System.err.println("Stub!");

}

}

每个线程要下载的文件路径,和要保存的路径,当然也可以从url中解析出文件名,然后再默认一个存放路径,这样就只要传一个参数了,但现在:一切从简。

private volatile boolean isFinish = false;//是否下载完成

设置一个标志位,用来表示是否下载完成 ,在判断池B是否要移除当前线程很重要。

public void start(){

new Thread(new Download()).start();

}

开启下载。

class Download implements Runnable {

private void downloadURL(){

InputStream is = null;

FileOutputStream fos = null;

try {

URL uri = new URL(url);

URLConnection conn = uri.openConnection();

conn.setDoInput(true);

conn.setConnectTimeout(10 * 1000);

is = conn.getInputStream();

fos = new FileOutputStream(file);

byte[] buffer = new byte[1024];

int length = 0;

while(-1 != (length = is.read(buffer))){

fos.write(buffer, 0, length);

}

is.close();

fos.flush();

fos.close();

} catch (MalformedURLException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

} finally {

if(null != is) {

try {

is.close();

} catch (IOException e) {

e.printStackTrace();

}

}

if(null != fos){

try {

fos.flush();

} catch (IOException e) {

e.printStackTrace();

}

try {

fos.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}

}

@Override

public void run() {

if(null != url && !("").equals(url) && null != file){

//开启下载到本地

System.out.println(System.currentTimeMillis() + "current thread start==>" + hashCode());

downloadURL();

System.out.println(System.currentTimeMillis() + "current thread end==>" + hashCode());

isFinish = true;

}

}

}

很简单,就是一个下载文件的线程。

线程池,其实包含了所有对线程的操作,没有再向上抽取一层了CustomThreadPool.java

private List customPool;// 线程池

private List startedPool; // 已开启的线程

定义两个线程池,也就是上面说的池A和池B

/** * 增加一个线程 * *@param thread */

public void addThread(DownloadThread thread) {

customPool.add(thread);

}

定义一个方法,把下载线程加到线程池中。

/** * 读取开启线程池 *@author Wicky * */

class StartPoolListener implements Runnable {

boolean isRun = true;

public void setIsRun(boolean isRun) {

this.isRun = isRun;

}

@Override

public void run() {

while (isRun) {

for (int i = 0; i < startedPool.size(); i++) {

DownloadThread thread = startedPool.get(i);

if (null != thread && thread.isFinish()) {

//手动保证集合并发安全

synchronized (thread) {

startedPool.remove(i);

}

}

}

}

}

}

另一个辅助线程,用来监听什么时候该移除线程,添加一个新线程了。

public void start() {

// 开启消费者监听

StartPoolListener lsn = new StartPoolListener();

Thread startPoolThread = new Thread(lsn);

startPoolThread.start();

// 不停的读取线程池中的线程,直到全部执行完成

while (customPool.size() > 0) {

// System.out.println("thread pool left==>" + customPool.size());

// 每次取最后一个线程

DownloadThread thread = customPool.get(customPool.size() - 1);

if (startedPool.size() < threadCount) {

// 开始线程

thread.start();

// 追加到已开启的线程中

startedPool.add(thread);

// 从原来的线程池中移除掉

customPool.remove(thread);

}

}

lsn.setIsRun(false);

startPoolThread.interrupt();

}

然后开始下载……这里分三块。

一、先开启一个监听线程

二、然后开始不停的读池A里的东西,如果发现池B中的数量小于指定的数量,就往里面添加一个新的线程

三、关闭监听线程

测试床MainTest.java

public static void main(String[] args){

String url1 = "http://f.hiphotos.baidu.com/image/pic/item/cc11728b4710b9125dcaf2ffc1fdfc03924522a3.jpg";

String url2 = "http://h.hiphotos.baidu.com/image/w%3D230/sign=f7bc78ce32adcbef013479059caf2e0e/5d6034a85edf8db1ec0d3d6e0a23dd54564e749c.jpg";

String url3 = "http://h.hiphotos.baidu.com/image/w%3D230/sign=56e13fbfaa014c08193b2fa63a7a025b/6a63f6246b600c3338816cde194c510fd9f9a128.jpg";

String url4 = "http://f.hiphotos.baidu.com/image/w%3D230/sign=e07d0317932397ddd6799f076983b216/8d5494eef01f3a29a8b0ef3e9b25bc315d607cc1.jpg";

String url5 = "http://c.hiphotos.baidu.com/image/pic/item/63d0f703918fa0ec4a43ba4c249759ee3c6ddbef.jpg";

String url6 = "http://h.hiphotos.baidu.com/image/pic/item/2e2eb9389b504fc2022d2904e7dde71190ef6d45.jpg";

String url7 = "http://g.hiphotos.baidu.com/image/w%3D230/sign=06cb213db8a1cd1105b675238912c8b0/d01373f082025aaf5a18bbc6f9edab64034f1a91.jpg";

DownloadThread thread1 = new DownloadThread(url1, "D:\\1.jpg");

DownloadThread thread2 = new DownloadThread(url2, "D:\\2.jpg");

DownloadThread thread3 = new DownloadThread(url3, "D:\\3.jpg");

DownloadThread thread4 = new DownloadThread(url4, "D:\\4.jpg");

DownloadThread thread5 = new DownloadThread(url5, "D:\\5.jpg");

DownloadThread thread6 = new DownloadThread(url6, "D:\\6.jpg");

DownloadThread thread7 = new DownloadThread(url7, "D:\\7.jpg");

CustomThreadPool pool = CustomThreadPool.getInstance(4);

pool.addThread(thread1);

pool.addThread(thread2);

pool.addThread(thread3);

pool.addThread(thread4);

pool.addThread(thread5);

pool.addThread(thread6);

pool.addThread(thread7);

//开启下载

pool.start();

}

线程操作还是有点不熟练,这里也只是提供下思路,肯定还会有更好的解决方案,大神勿喷

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我可以为您提供一段Java代码实现线程下载: ```java import java.io.InputStream; import java.io.RandomAccessFile; import java.net.HttpURLConnection; import java.net.URL; public class MultiThreadDownload extends Thread { private String url; // 文件下载地址 private String filePath; // 保存路径 private int threadNum; // 线程数量 public MultiThreadDownload(String url, String filePath, int threadNum) { this.url = url; this.filePath = filePath; this.threadNum = threadNum; } @Override public void run() { try { URL downloadUrl = new URL(url); HttpURLConnection httpURLConnection = (HttpURLConnection) downloadUrl.openConnection(); httpURLConnection.setRequestMethod("GET"); httpURLConnection.setConnectTimeout(5000); int fileLength = httpURLConnection.getContentLength(); int blockSize = fileLength % threadNum == 0 ? fileLength / threadNum : fileLength / threadNum + 1; RandomAccessFile randomAccessFile = new RandomAccessFile(filePath, "rw"); randomAccessFile.setLength(fileLength); randomAccessFile.close(); for (int i = 0; i < threadNum; i++) { int startIndex = i * blockSize; int endIndex = (i + 1) * blockSize - 1; if (endIndex > fileLength) endIndex = fileLength; new DownloadThread(url, filePath, startIndex, endIndex).start(); } } catch (Exception e) { e.printStackTrace(); } } private class DownloadThread extends Thread { private String url; private String filePath; private int startIndex; private int endIndex; public DownloadThread(String url, String filePath, int startIndex, int endIndex) { this.url = url; this.filePath = filePath; this.startIndex = startIndex; this.endIndex = endIndex; } @Override public void run() { try { RandomAccessFile randomAccessFile = new RandomAccessFile(filePath, "rw"); randomAccessFile.seek(startIndex); URL downloadUrl = new URL(url); HttpURLConnection httpURLConnection = (HttpURLConnection) downloadUrl.openConnection(); httpURLConnection.setRequestMethod("GET"); httpURLConnection.setConnectTimeout(5000); httpURLConnection.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex); InputStream inputStream = httpURLConnection.getInputStream(); byte[] buffer = new byte[1024]; int length = -1; while ((length = inputStream.read(buffer)) != -1) { randomAccessFile.write(buffer, 0, length); } inputStream.close(); randomAccessFile.close(); } catch (Exception e) { e.printStackTrace(); } } } } ``` 这段代码利用多线程实现文件下载,具体实现过程为:首先获取文件长度,然后根据线程数量计算每个线程需要下载文件块大小,接着创建多个下载线程,每个下载线程负责下载自己对应的文件块。在下载时,通过设置请求头中的 `Range` 参数实现分块下载,最终将所有分块拼接起来得到完整的文件

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值