java線程池ExecutorService超時處理小結(jié)
場景問題:使用線程池ExecutorService,想設(shè)置每個子線程的執(zhí)行超時時間,使用future.get()來監(jiān)聽超時,當(dāng)有子線程阻塞時,導(dǎo)致有的隊(duì)列任務(wù)還未執(zhí)行就被取消了。
方式一、使用 future.get() 來監(jiān)聽超時取消
這種辦法看似能解決問題,但是當(dāng)任務(wù)累積處理不過來時,會漏執(zhí)行。
比如下面的例子,就實(shí)際只會執(zhí)行一個子線程。
package com.study;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.*;
public class Test {
private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
private static final ExecutorService threadPool = Executors.newFixedThreadPool(1);
public static void main(String[] args) throws Exception {
for (int i = 0; i < 10; i++) {
Future<?> future = threadPool.submit(new Runnable() {
@Override
public void run() {
try {
System.out.println(LocalDateTime.now().format(formatter));
Thread.sleep(5000);
} catch (InterruptedException e) {
// e.printStackTrace();
}
}
});
new Thread(new Runnable() {
@Override
public void run() {
try {
future.get(3, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (TimeoutException e) {//超時異常
future.cancel(true); // 超時后取消任務(wù)
}
}
}).start();
}
}
}
方式二、在子線程內(nèi)部,超時后去發(fā)送中斷信號
package com.study;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.*;
public class Test {
private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
private static final ExecutorService threadPool = Executors.newFixedThreadPool(1);
private static final ScheduledExecutorService timeoutExecutor = new ScheduledThreadPoolExecutor(1);//監(jiān)聽超時,這個數(shù)量要和線程池數(shù)量相同
public static void main(String[] args) throws Exception {
for (int i = 0; i < 10; i++) {
// int delay = 3;
int delay = i + 1;
threadPool.submit(new Runnable() {
@Override
public void run() {
ScheduledFuture<?> schedule = null;
try {
Thread thread = Thread.currentThread();
// 啟動一個定時器,如果任務(wù)執(zhí)行超過3秒則中斷當(dāng)前線程
schedule = timeoutExecutor.schedule(() -> {
thread.interrupt(); // 中斷當(dāng)前正在執(zhí)行的任務(wù)
}, delay, TimeUnit.SECONDS);
System.out.println(LocalDateTime.now().format(formatter));
Thread.sleep(5000);
// FileOutputStream fos = new FileOutputStream("d:/test.txt" + k);
// for (int j = 0; j < 1000000; j++) {
// fos.write("123".getBytes());
// }
// fos.close();
} catch (InterruptedException e) {
// e.printStackTrace();
} finally {
if (schedule != null) {
//取消任務(wù)
schedule.cancel(true);
}
}
}
});
}
}
}
這里其實(shí)還是有問題 ,把 Thread.sleep(5000);改成注釋的io阻塞,還是要等線程執(zhí)行結(jié)束后才會取消線程執(zhí)行。
所以單純使用 future 是實(shí)現(xiàn)不了這個場景的邏輯的。
timeoutExecutor 數(shù)量和 線程池數(shù)量要一致的原因如下示例。
package com.study;
import java.time.LocalDateTime;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ScheduledExecutorServiceExample {
private static final ScheduledExecutorService timeoutExecutor = new ScheduledThreadPoolExecutor(2);
public static void main(String[] args) throws InterruptedException {
// 調(diào)用schedule方法兩次
scheduleTask("Task 1");
scheduleTask("Task 2");
scheduleTask("Task 3");
}
private static void scheduleTask(String taskName) {
timeoutExecutor.schedule(() -> {
System.out.println(taskName + " started at: " + LocalDateTime.now());
try {
// 模擬任務(wù)執(zhí)行
Thread.sleep(2000); // 假設(shè)每個任務(wù)執(zhí)行2秒
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, 3, TimeUnit.SECONDS);
}
}方式三、自己定義鎖來實(shí)現(xiàn)
package com.study;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.*;
public class Test {
private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
private static final ExecutorService threadPool = Executors.newFixedThreadPool(16);//這里不能設(shè)置為1了,這里已經(jīng)不是用來控制并發(fā)數(shù)量了,只是為了重復(fù)利用線程
private static final ScheduledExecutorService timeoutExecutor = new ScheduledThreadPoolExecutor(1);//監(jiān)聽超時,這個數(shù)量要和線程池數(shù)量相同
public static void main(String[] args) throws Exception {
for (int i = 0; i < 10; i++) {
Thread.sleep(50);
// int delay = 3;
int delay = i + 1;
int k = i;
threadPool.submit(new Runnable() {
@Override
public void run() {
ScheduledFuture<?> schedule = null;
try {
ThreadPool.awaitThread();
// 啟動一個定時器,如果任務(wù)執(zhí)行超過3秒則中斷當(dāng)前線程
// timeoutExecutor如果只有一個線程池,這里面的代碼片段會阻塞,上一個線程在這里的代碼片段執(zhí)行完后,當(dāng)前線程才會執(zhí)行這里的代碼片段,
// 但是影響不大,因?yàn)檫@里的代碼片段只是釋放動作,一瞬間就會執(zhí)行完,所以影響不大,
// 如果其他場景這里阻塞時間比較久,那么timeoutExecutor線程大小要和threadPool線程大小一致。
schedule = timeoutExecutor.schedule(() -> {
System.out.println("釋放1");
ThreadPool.releaseThread();
}, delay, TimeUnit.SECONDS);
System.out.println("【" + Thread.currentThread().getName() + "】" + LocalDateTime.now().format(formatter));
Thread.sleep(5000);
// FileOutputStream fos = new FileOutputStream("d:/test.txt" + k);
// for (int j = 0; j < 1000000; j++) {
// fos.write("123".getBytes());
// }
// fos.close();
} catch (Exception e) {
System.out.println("異常");
} finally {
if (schedule != null) {
//cancel返回true任務(wù)還未執(zhí)行,需要取消任務(wù)
if (schedule.cancel(true)) {
System.out.println("釋放2");
ThreadPool.releaseThread();
}
}
}
}
});
}
}
}package com.study;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 線程池
*/
public class ThreadPool {
private static final int MAX_POOL_SIZE = 1; // 最大線程數(shù),控制并發(fā)數(shù)量
private static int totalThread = 0; // 總線程數(shù)
private static final Lock lock = new ReentrantLock(true);
private static final Condition notice = lock.newCondition();
/**
* 從線程池獲取線程
*/
public static boolean awaitThread() {
lock.lock();
try {
// 嘗試從線程池中獲取線程
if (totalThread < MAX_POOL_SIZE) {
totalThread++;
return true;
}
// 線程已到達(dá)最大線程數(shù),等待歸還線程,最長等待1小時,await()會釋放當(dāng)前線程的鎖
if (notice.await(1, TimeUnit.HOURS)) {
totalThread++;
return true;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
return false;
}
/**
* 釋放線程到線程池
*/
public static void releaseThread() {
lock.lock();
try {
totalThread--;
// 通知有空閑,signal()會喚醒其中一個await()線程
notice.signal();
} finally {
lock.unlock();
}
}
}到此這篇關(guān)于java線程池ExecutorService超時處理小結(jié)的文章就介紹到這了,更多相關(guān)java線程池ExecutorService超時內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Intellij idea下使用不同tomcat編譯maven項(xiàng)目的服務(wù)器路徑方法詳解
今天小編就為大家分享一篇關(guān)于Intellij idea下使用不同tomcat編譯maven項(xiàng)目的服務(wù)器路徑方法詳解,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2019-02-02
Java多線程編程之讀寫鎖ReadWriteLock用法實(shí)例
這篇文章主要介紹了Java多線程編程之讀寫鎖ReadWriteLock用法實(shí)例,本文直接給出編碼實(shí)例,需要的朋友可以參考下2015-05-05
Spring Data Redis中Hash結(jié)構(gòu)操作技巧與最佳實(shí)踐
本文全面掌握Spring Data Redis中Hash結(jié)構(gòu)的操作技巧與最佳實(shí)踐,包括節(jié)省內(nèi)存、支持單字段更新和查詢、原子性操作等,通過SpringDataRedis的opsForHash(),可以方便地進(jìn)行Hash結(jié)構(gòu)的操作,感興趣的朋友跟隨小編一起看看吧2026-01-01
SpringBoot整合Kotlin構(gòu)建Web服務(wù)的方法示例
這篇文章主要介紹了SpringBoot整合Kotlin構(gòu)建Web服務(wù)的方法示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-02-02
使用springboot activiti關(guān)閉驗(yàn)證自動部署方式
這篇文章主要介紹了使用springboot activiti關(guān)閉驗(yàn)證自動部署方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-09-09
mybatis?like模糊查詢特殊字符報錯轉(zhuǎn)義處理方式
這篇文章主要介紹了mybatis?like模糊查詢特殊字符報錯轉(zhuǎn)義處理方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-01-01

