मैं एक ऐसे मुद्दे पर चल रहा हूं, जहां अगर मैं ThreadPoolExecutorपूल बनने के बाद एक अलग संख्या में एक कोर पूल आकार का आकार बदलने का प्रयास करता हूं , तो रुक-रुक कर, कुछ कार्यों को अस्वीकार कर दिया जाता है RejectedExecutionException, हालांकि मैं कभी भी queueSize + maxPoolSizeकार्यों की संख्या से अधिक जमा नहीं करता ।
मैं जिस समस्या को हल करने की कोशिश कर रहा हूं, वह यह है कि ThreadPoolExecutorथ्रेड पूल की कतार में बैठे लंबित निष्पादन के आधार पर इसके मूल थ्रेड्स का विस्तार करना। मुझे इसकी आवश्यकता है क्योंकि डिफ़ॉल्ट रूप से ThreadPoolExecutorएक नया Threadतभी बनेगा जब कतार भरी होगी।
यहां एक छोटा स्व-निहित शुद्ध जावा 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();
}
}
अगर मैं कभी भी अधिक क्यूपेसिटी + मैक्सट्रेड्स जमा नहीं करता हूं, तो पूल को कभी भी रिजेक्टेड एक्सेप्शन एक्ससेप्शन फेंकना चाहिए। मैं थ्रेडपूल एक्ज़ीक्यूटर की परिभाषा से अधिकतम थ्रेड्स को कभी नहीं बदल रहा हूं, इसे या तो थ्रेड में या क्यू में कार्य को समायोजित करना चाहिए।
बेशक, अगर मैं कभी पूल का आकार नहीं बदलता हूं, तो थ्रेड पूल कभी भी किसी भी प्रस्तुतियाँ को अस्वीकार नहीं करता है। सबमिशन में किसी भी तरह की देरी को शामिल करने के बाद से डिबग करना भी मुश्किल हो जाता है।
RejectExecutionException को ठीक करने के बारे में कोई संकेत?
ThreadPoolExecutorशायद बहुत बुरा विचार है, और क्या आपको इस मामले में मौजूदा कोड को बदलने की आवश्यकता नहीं है? यह सबसे अच्छा होगा कि आप कुछ उदाहरण प्रदान करें कि आपका वास्तविक कोड निष्पादक तक कैसे पहुँचता है। मुझे आश्चर्य होगा अगर यह कई विधियों का उपयोग करता है ThreadPoolExecutor(यानी नहीं में ExecutorService)।
ExecutorServiceकिसी मौजूदा को लपेटकर अपना स्वयं का कार्यान्वयन क्यों नहीं प्रदान करता है, जो उन कार्यों को फिर से शुरू करता है जो कि आकार बदलने के कारण प्रस्तुत करने में विफल रहे?