錯(cuò)誤描述:
????????我設(shè)置了CountDownLatch對線程的協(xié)作做出了一些限制,但是我發(fā)現(xiàn)運(yùn)行一段時(shí)間以后便發(fā)現(xiàn)定時(shí)任務(wù)不運(yùn)行了。
具體代碼:
public void sendToCertainWeb() throws IOException, InterruptedException {
List<String> urlList = scheduleplanMapper.getRandomUrlList();
Thread.sleep(6000);
CountDownLatch countDownLatch = new CountDownLatch(20);
for (String s : urlList) {
transportThreadPool.execute(()->{
try {
URL url = new URL(s);
// 打開連接
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// 設(shè)置請求方法為GET
connection.setRequestMethod("GET");
connection.setConnectTimeout(100000);
connection.setReadTimeout(100000);
// 添加自定義的請求頭信息
String agent = scheduleplanMapper.getRandomAgent();
connection.addRequestProperty("User-Agent", agent);
connection.addRequestProperty("Accept-Language", "en-US,en;q=0.9");
// 獲取服務(wù)器返回的狀態(tài)碼
int responseCode = connection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
// 讀取服務(wù)器返回的數(shù)據(jù)
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String line;
StringBuilder response = new StringBuilder();
while ((line = reader.readLine()) != null) {
response.append(line);
}
reader.close();
log.info("Right Code: " + responseCode);
} else {
log.error("Error Code: " + responseCode);
}
// 關(guān)閉連接
connection.disconnect();
countDownLatch.countDown();
}catch (Exception e){
log.error(JSON.toJSONString(e));
}
});
}
countDownLatch.await();
}
報(bào)錯(cuò)以后定時(shí)任務(wù)不運(yùn)行了?
錯(cuò)誤排查:
?打印線程日志發(fā)現(xiàn)定時(shí)任務(wù)的線程在第86行代碼停著不動(dòng)了。
正常的線程日志應(yīng)該是這樣的。
查看第86行代碼,發(fā)現(xiàn)這里并沒有喚醒主線程 ,導(dǎo)致線程一直處于運(yùn)行狀態(tài),無法繼續(xù)下一個(gè)任務(wù)。
????????錯(cuò)誤的原因是countDownLatch.countDown()并沒有放在finally塊里因此發(fā)生了錯(cuò)誤并不會(huì)走這塊代碼,導(dǎo)致線程沒有countDown
錯(cuò)誤修改:
把countDownLatch.countDown();放在finally代碼塊里保證一定會(huì)進(jìn)行countDown這個(gè)動(dòng)作
正確代碼:
public void sendToCertainWeb() throws IOException, InterruptedException {
List<String> urlList = scheduleplanMapper.getRandomUrlList();
Thread.sleep(6000);
CountDownLatch countDownLatch = new CountDownLatch(20);
for (String s : urlList) {
transportThreadPool.execute(()->{
try {
URL url = new URL(s);
// 打開連接
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// 設(shè)置請求方法為GET
connection.setRequestMethod("GET");
connection.setConnectTimeout(100000);
connection.setReadTimeout(100000);
// 添加自定義的請求頭信息
String agent = scheduleplanMapper.getRandomAgent();
connection.addRequestProperty("User-Agent", agent);
connection.addRequestProperty("Accept-Language", "en-US,en;q=0.9");
// 獲取服務(wù)器返回的狀態(tài)碼
int responseCode = connection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
// 讀取服務(wù)器返回的數(shù)據(jù)
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String line;
StringBuilder response = new StringBuilder();
while ((line = reader.readLine()) != null) {
response.append(line);
}
reader.close();
log.info("Right Code: " + responseCode);
} else {
log.error("Error Code: " + responseCode);
}
// 關(guān)閉連接
connection.disconnect();
}catch (Exception e){
log.error(JSON.toJSONString(e));
}finally {
countDownLatch.countDown();
}
});
}
countDownLatch.await();
}
錯(cuò)誤總結(jié):
?????????我們一般認(rèn)為線程處于blocked狀態(tài)的時(shí)候線程才是處于阻塞狀態(tài),但是這個(gè)狀態(tài)只是對于計(jì)算機(jī)來說的。對于我們來說,只要業(yè)務(wù)不執(zhí)行了,線程就是處于阻塞狀態(tài)的,因此任何狀態(tài)下的線程對于業(yè)務(wù)來說都是阻塞的。 我這個(gè)項(xiàng)目是爬蟲項(xiàng)目,會(huì)去爬取別人網(wǎng)站的數(shù)據(jù),有些網(wǎng)站識(shí)別爬蟲之后不僅會(huì)拒絕你訪問,還會(huì)通過一直不給響應(yīng)使得你的服務(wù)器線程占滿,進(jìn)而導(dǎo)致你的爬蟲服務(wù)器崩潰。文章來源:http://www.zghlxwxcb.cn/news/detail-856963.html
參考文章:?
未設(shè)置超時(shí)時(shí)間導(dǎo)致線程池資源耗盡,排查過程-CSDN博客文章來源地址http://www.zghlxwxcb.cn/news/detail-856963.html
到了這里,關(guān)于CountDownLatch使用錯(cuò)誤+未最終斷開連接導(dǎo)致線程池資源耗盡的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!