ฉันกำลังประสบปัญหาซึ่งถ้าฉันพยายามปรับขนาดThreadPoolExecutor
แกนกลางของสระว่ายน้ำให้เป็นจำนวนที่แตกต่างกันหลังจากที่สร้างกลุ่มนั้นเป็นระยะ ๆ งานบางอย่างถูกปฏิเสธด้วยงานRejectedExecutionException
แม้ว่าฉันจะไม่ส่งงานมากกว่าqueueSize + maxPoolSize
จำนวน
ปัญหาที่ฉันพยายามแก้ไขคือการขยายThreadPoolExecutor
ที่ปรับขนาดเธรดหลักตามการดำเนินการที่ค้างอยู่ซึ่งอยู่ในคิวของเธรดพูล ฉันต้องการสิ่งนี้เพราะโดยปกติแล้ว a ThreadPoolExecutor
จะสร้างใหม่Thread
เฉพาะเมื่อคิวเต็ม
นี่คือโปรแกรม Pure Java 8 ขนาดเล็กที่มีในตัวเองซึ่งแสดงให้เห็นถึงปัญหา
import static java.lang.Math.max;
import static java.lang.Math.min;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadPoolResizeTest {
public static void main(String[] args) throws Exception {
// increase the number of iterations if unable to reproduce
// for me 100 iterations have been enough
int numberOfExecutions = 100;
for (int i = 1; i <= numberOfExecutions; i++) {
executeOnce();
}
}
private static void executeOnce() throws Exception {
int minThreads = 1;
int maxThreads = 5;
int queueCapacity = 10;
ThreadPoolExecutor pool = new ThreadPoolExecutor(
minThreads, maxThreads,
0, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(queueCapacity),
new ThreadPoolExecutor.AbortPolicy()
);
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
scheduler.scheduleAtFixedRate(() -> resizeThreadPool(pool, minThreads, maxThreads),
0, 10, TimeUnit.MILLISECONDS);
CompletableFuture<Void> taskBlocker = new CompletableFuture<>();
try {
int totalTasksToSubmit = queueCapacity + maxThreads;
for (int i = 1; i <= totalTasksToSubmit; i++) {
// following line sometimes throws a RejectedExecutionException
pool.submit(() -> {
// block the thread and prevent it from completing the task
taskBlocker.join();
});
// Thread.sleep(10); //enabling even a small sleep makes the problem go away
}
} finally {
taskBlocker.complete(null);
scheduler.shutdown();
pool.shutdown();
}
}
/**
* Resize the thread pool if the number of pending tasks are non-zero.
*/
private static void resizeThreadPool(ThreadPoolExecutor pool, int minThreads, int maxThreads) {
int pendingExecutions = pool.getQueue().size();
int approximateRunningExecutions = pool.getActiveCount();
/*
* New core thread count should be the sum of pending and currently executing tasks
* with an upper bound of maxThreads and a lower bound of minThreads.
*/
int newThreadCount = min(maxThreads, max(minThreads, pendingExecutions + approximateRunningExecutions));
pool.setCorePoolSize(newThreadCount);
pool.prestartAllCoreThreads();
}
}
เหตุใดพูลจึงควรโยน RejectedExecutionException หากฉันไม่เคยส่งเพิ่มเติมว่า queueCapacity + maxThreads ฉันไม่เคยเปลี่ยนเธรดสูงสุดดังนั้นตามคำจำกัดความของ ThreadPoolExecutor มันควรรองรับงานในเธรดหรือคิว
แน่นอนถ้าฉันไม่เคยปรับขนาดพูลสระเธรดจะไม่ปฏิเสธการส่งใด ๆ นอกจากนี้ยังเป็นเรื่องยากที่จะแก้ปัญหาตั้งแต่การเพิ่มความล่าช้าใด ๆ ในการส่งทำให้ปัญหาหายไป
ตัวชี้ใด ๆ เกี่ยวกับวิธีแก้ไข RejectedExecutionException
ThreadPoolExecutor
อาจเป็นแนวคิดที่ไม่ดีและคุณไม่จำเป็นต้องเปลี่ยนรหัสที่มีอยู่ในกรณีนี้ด้วยใช่ไหม มันจะเป็นการดีที่สุดถ้าคุณให้ตัวอย่างว่าโค้ดจริงของคุณเข้าถึงผู้ดำเนินการ ฉันจะแปลกใจถ้ามันใช้หลายวิธีเฉพาะThreadPoolExecutor
(เช่นไม่ได้อยู่ในExecutorService
)
ExecutorService
โดยห่อสิ่งที่มีอยู่ซึ่งส่งงานที่ล้มเหลวในการส่งอีกครั้งเนื่องจากการปรับขนาด?