java線程池實(shí)現(xiàn)批量下載文件
本文實(shí)例為大家分享了java線程池實(shí)現(xiàn)批量下載文件的具體代碼,供大家參考,具體內(nèi)容如下
1 創(chuàng)建線程池
package com.cheng.webb.thread; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; public class ThreadUtil { /** * 創(chuàng)建批量下載線程池 * * @param threadSize 下載線程數(shù) * @return ExecutorService */ public static ExecutorService buildDownloadBatchThreadPool(int threadSize) { int keepAlive = 0; String prefix = "download-batch"; ThreadFactory factory = ThreadUtil.buildThreadFactory(prefix); return new ThreadPoolExecutor(threadSize, threadSize, keepAlive, TimeUnit.SECONDS, new ArrayBlockingQueue<>(threadSize), factory); } /** * 創(chuàng)建自定義線程工廠 * * @param prefix 名稱前綴 * @return ThreadFactory */ public static ThreadFactory buildThreadFactory(String prefix) { return new CustomThreadFactory(prefix); } /** * 自定義線程工廠 */ public static class CustomThreadFactory implements ThreadFactory { private String threadNamePrefix; private AtomicInteger counter = new AtomicInteger(1); /** * 自定義線程工廠 * * @param threadNamePrefix 工廠名稱前綴 */ CustomThreadFactory(String threadNamePrefix) { this.threadNamePrefix = threadNamePrefix; } @Override public Thread newThread(Runnable r) { String threadName = threadNamePrefix + "-t" + counter.getAndIncrement(); return new Thread(r, threadName); } } }
2 批量下載文件
package com.cheng.webb.thread; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.*; /** * 文件下載類 * * @author shucheng * @creation 2019年1月30日下午4:41:32 */ public class DownloadUtil { private static Logger logger = LoggerFactory.getLogger(DownloadUtil.class); /** * 下載線程數(shù) */ private static final int DOWNLOAD_THREAD_NUM = 14; /** * 下載線程池 */ private static ExecutorService downloadExecutorService = ThreadUtil .buildDownloadBatchThreadPool(DOWNLOAD_THREAD_NUM); /** * 文件下載 * * @param fileUrl * 文件url,如:<code>https://img3.doubanio.com//view//photo//s_ratio_poster//public//p2369390663.webp</code> * @param path * 存放路徑,如: /opt/img/douban/my.webp */ public static void download(String fileUrl, String path) { // 判斷存儲(chǔ)文件夾是否已經(jīng)存在或者創(chuàng)建成功 if (!createFolderIfNotExists(path)) { logger.error("We can't create folder:{}", getFolder(path)); return; } InputStream in = null; FileOutputStream out = null; try { URL url = new URL(fileUrl); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); // 2s conn.setConnectTimeout(10000); in = conn.getInputStream(); out = new FileOutputStream(path); int len; byte[] arr = new byte[1024 * 1000]; while (-1 != (len = in.read(arr))) { out.write(arr, 0, len); } out.flush(); conn.disconnect(); } catch (Exception e) { logger.error("Fail to download: {} by {}", fileUrl, e.getMessage()); } finally { try { if (null != out) { out.close(); } if (null != in) { in.close(); } } catch (Exception e) { // do nothing } } } /** * 創(chuàng)建文件夾,如果文件夾已經(jīng)存在或者創(chuàng)建成功返回true * * @param path * 路徑 * @return boolean */ private static boolean createFolderIfNotExists(String path) { String folderName = getFolder(path); if (folderName.equals(path)) { return true; } File folder = new File(getFolder(path)); if (!folder.exists()) { synchronized (DownloadUtil.class) { if (!folder.exists()) { return folder.mkdirs(); } } } return true; } /** * 獲取文件夾 * * @param path * 文件路徑 * @return String */ private static String getFolder(String path) { int index = path.lastIndexOf("/"); return -1 != index ? path.substring(0, index) : path; } /** * 下載資源 * <p> * issue: 線程池創(chuàng)建過(guò)多 * <p> * 最大批量下載為5,請(qǐng)知悉 * * @param resourceMap * 資源map, key為資源下載url,value為資源存儲(chǔ)位置 */ public static void batch(Map<String, String> resourceMap) { if (resourceMap == null || resourceMap.isEmpty()) { return; } try { List<String> keys = new ArrayList<>(resourceMap.keySet()); int size = keys.size(); int pageNum = getPageNum(size); for (int index = 0; index < pageNum; index++) { int start = index * DOWNLOAD_THREAD_NUM; int last = getLastNum(size, start + DOWNLOAD_THREAD_NUM); final CountDownLatch latch = new CountDownLatch(last - start); // 獲取列表子集 List<String> urlList = keys.subList(start, last); for (String url : urlList) { // 提交任務(wù) Runnable task = new DownloadWorker(latch, url, resourceMap.get(url)); downloadExecutorService.submit(task); } latch.await(); } } catch (Exception e) { logger.error("{}", e); } logger.info("Download resource map is all done"); } /** * 獲取最后一個(gè)元素 * * @param size * 列表長(zhǎng)度 * @param index * 下標(biāo) * @return int */ private static int getLastNum(int size, int index) { return index > size ? size : index; } /** * 獲取劃分頁(yè)面數(shù)量 * * @param size * 列表長(zhǎng)度 * @return int */ private static int getPageNum(int size) { int tmp = size / DOWNLOAD_THREAD_NUM; return size % DOWNLOAD_THREAD_NUM == 0 ? tmp : tmp + 1; } /** * 下載線程 */ static class DownloadWorker implements Runnable { private CountDownLatch latch; private String url; private String path; DownloadWorker(CountDownLatch latch, String url, String path) { this.latch = latch; this.url = url; this.path = path; } @Override public void run() { logger.debug("Start batch:[{}] into: [{}]", url, path); DownloadUtil.download(url, path); logger.debug("Download:[{}] into: [{}] is done", url, path); latch.countDown(); } } }
3 測(cè)試批量下載文件
package com.cheng.webb.thread; import java.util.HashMap; import java.util.Map; import org.junit.Test; import com.alibaba.fastjson.JSON; public class DownLoadTest { String json = "{\r\n" + " \"http://www.xxx.com/111/123.mp4\":\"myFile/111/123.mp4\",\r\n" + " \"http://www.xxx.com/111/124.mp4\":\"myFile/111/124.mp4\",\r\n" + " \"http://www.xxx.com/111/125.mp4\":\"myFile/111/125.mp4\"\r\n" + "}"; @SuppressWarnings("unchecked") @Test public void test() { Map<String, String> map = new HashMap<>(); Map<String, String> resMap = JSON.parseObject(json, map.getClass()); int times = 1; for (int index = 0; index < times; index++) { DownloadUtil.batch(resMap); } } }
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Java中的StampedLock實(shí)現(xiàn)原理詳解
這篇文章主要介紹了Java中的StampedLock實(shí)現(xiàn)原理詳解,ReentrantReadWriteLock采用悲觀讀,第一個(gè)讀線程拿到鎖后,第二個(gè)/第三個(gè)讀線程可以拿到鎖,特別是在讀線程很多,寫線程很少時(shí),需要的朋友可以參考下2024-01-01如何在IDE部署springboot項(xiàng)目(有swagger和無(wú)swagger都是一樣的)到服務(wù)器或者虛擬機(jī)上的docke
這篇文章主要介紹了如何在IDE部署springboot項(xiàng)目(有swagger和無(wú)swagger都是一樣的)到服務(wù)器或者虛擬機(jī)上的docker,本文給大家分享我的安裝歷程,需要的朋友可以參考下2023-01-01Spring系列中的beanFactory與ApplicationContext
這篇文章主要介紹了Spring系列中的beanFactory與ApplicationContext,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-09-09java實(shí)現(xiàn)獲取安卓設(shè)備里已安裝的軟件包
本文給大家介紹的是如何獲取設(shè)備中已經(jīng)安裝的應(yīng)用軟件包的代碼,其核心方法原理很簡(jiǎn)單,我們通過(guò)Android中提供的PackageManager類,來(lái)獲取手機(jī)中安裝的應(yīng)用程序信息2015-10-10java獲取和設(shè)置系統(tǒng)變量問(wèn)題(環(huán)境變量)
這篇文章主要介紹了java獲取和設(shè)置系統(tǒng)變量問(wèn)題(環(huán)境變量),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-01-01Java實(shí)現(xiàn)利用廣度優(yōu)先遍歷(BFS)計(jì)算最短路徑的方法
這篇文章主要介紹了Java實(shí)現(xiàn)利用廣度優(yōu)先遍歷(BFS)計(jì)算最短路徑的方法,實(shí)例分析了廣度優(yōu)先遍歷算法的原理與使用技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-04-04使用InputStream的available()能否用來(lái)判斷當(dāng)前流是否讀取到文件
這篇文章主要介紹了使用InputStream的available()能否用來(lái)判斷當(dāng)前流是否讀取到文件問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-06-06JavaMail實(shí)現(xiàn)郵件發(fā)送的方法
這篇文章主要介紹了JavaMail實(shí)現(xiàn)郵件發(fā)送的方法,實(shí)例分析了java實(shí)現(xiàn)郵件發(fā)送的相關(guān)技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2015-04-04SpringBoot Redis用注釋實(shí)現(xiàn)接口限流詳解
Redis 除了做緩存,還能干很多很多事情:分布式鎖、限流、處理請(qǐng)求接口冪等性。。。太多太多了~今天想和小伙伴們聊聊用 Redis 處理接口限流,這也是最近的 項(xiàng)目涉及到這個(gè)知識(shí)點(diǎn)了,我就拎出來(lái)和大家聊聊這個(gè)話題2022-07-07