問題記錄:執行緒池批量下載檔案
阿新 • • 發佈:2018-12-09
在一個視訊網上找到了喜歡的線上視訊資源,沒有下載按鈕,只能自己下載了,看了 一下network,是ts分片的檔案,好在是命名挺規範的,都是 xxxx + index + .ts的格式,方便了 我下載。
一開始我用單執行緒下載,下了半天只下了200個 分片,總共有800個,因此想到了執行緒池,一直以為我是會用執行緒池的,結果才發現了自己的不足,記錄一下。
構造器
/*和資料庫連線池很像*/ ThreadPoolExecutor pool = new ThreadPoolExecutor(1, // 小於corePoolSize時即使其他執行緒空閒也會建立 5, // 如果當前執行緒都忙,但是小於匯流排程,會建立新的 1000, // 空閒(非核心)執行緒存活時間 TimeUnit.MINUTES, new ArrayBlockingQueue<>(5)); // 如果使用synchronousQueue 執行緒最多隻能達到corePoolSize 舉個例子,我使用了 ExecutorService service = Executors.newFixedThreadPool(4); 內部就是: public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); // 任務佇列是無界的,因此不需要在意拒絕策略。 } 說到 拒絕 策略,一共有4種 /** * maximumPoolSize有限,等待任務佇列使用的是有界佇列,倘若是linkedBlockQueue總是排隊 * 預設AbortPolicy 丟擲異常 * Discard 忽略新任務, 不報錯 * DiscardOld 取消舊任務 * CallerRunsPolicy 在提交執行緒中執行任務 */
下面進入正題
異常
我的程式借用了httpclient
HttpClient httpClient= new DefaultHttpClient(); ExecutorService service = Executors.newFixedThreadPool(4); Collection<Callable<Integer>> futureTasks = new LinkedList<>(); for (int i = 1; i <= totalPierce; i++) { final int v = i; futureTasks.add(() -> { download(httpClient, v); return v; }); } List<Future<Integer>> futureList = service.invokeAll(futureTasks); private static void download(HttpClient client, int i) { System.out.println("連線" + i + "ts"); HttpGet httpget = new HttpGet(makeURL(i)); try { HttpResponse response = client.execute(httpget); HttpEntity entity = response.getEntity(); String file = "F:\\seg-" + i + ".ts"; FileOutputStream os = new FileOutputStream(file); entity.writeTo(os); os.close(); System.out.println("完成" + i); } catch (IOException e) { e.printStackTrace(); } }
執行結果也特別奇怪,先輸出1,2,3,4,阻塞 一段時間,然後 一股腦的輸出連線直到 totalPierce.但是沒有顯示完成的,開了VisualVM,執行緒都是正常的,我就猜想 是不是哪裡 出現問題,搜了 一下,參考了這篇文章:
連線
按照前輩所說,不適合設定異常處理類來處理 ,而且我這裡只是 個人小玩具 ,有三個辦法:
- 異常處理直接列印到控制檯,但是如果多個執行緒同時列印 ,輸出可能是亂 的
- 列印到日誌,我比較懶,不想再拉配置檔案和新增依賴,pass
- 對於結果進行get,在get處進行異常處理
原來如此,如果futureTask異常,future.get時是會 丟擲的!
新增如下程式碼 :
for (Future f :
futureList) {
try {
f.get();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
這才發現 defaultHttpclient不是執行緒安全的!
因此改用
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
cm.setMaxTotal(100);
CloseableHttpClient httpClient = HttpClients.custom()
.setConnectionManager(cm)
.build();
暫時沒有問題,但是還沒有完全好,我仍然少考慮了很多東西,比如異常的分片需要 重試?下載速度仍然沒有達到很高,追憶前輩們proxyee-down IDM等等的下載工具,真是高山仰止,還需努力
附上httpClient依賴:
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.3</version>
</dependency>