मैं इस सीमा के आसपास कैसे काम कर सकता हूं, ThreadPoolExecutor
जहां अधिक धागे शुरू करने से पहले कतार को बाध्य करना होगा और पूर्ण करना होगा।
मेरा मानना है कि मैंने आखिरकार इस सीमा तक कुछ हद तक सुरुचिपूर्ण (शायद थोड़ा हैसी) समाधान पाया है ThreadPoolExecutor
। इसमें यह शामिल LinkedBlockingQueue
है कि जब कुछ कार्य पहले से ही कतारबद्ध हों false
, queue.offer(...)
तब इसे वापस करना होगा। यदि मौजूदा थ्रेड्स पंक्तिबद्ध कार्यों के साथ नहीं चल रहे हैं, तो TPE अतिरिक्त थ्रेड्स जोड़ देगा। यदि पूल पहले से ही अधिकतम धागे पर है, तो RejectedExecutionHandler
वसीयत को बुलाया जाएगा। यह हैंडलर है जो तब put(...)
कतार में करता है ।
यह निश्चित रूप से एक कतार लिखना अजीब है जहां offer(...)
वापस आ सकते हैं false
और put()
कभी भी ब्लॉक नहीं करते हैं ताकि हैक हिस्सा हो। लेकिन यह TPE कतार के उपयोग के साथ अच्छी तरह से काम करता है इसलिए मुझे ऐसा करने में कोई समस्या नहीं दिखती है।
यहाँ कोड है:
// extend LinkedBlockingQueue to force offer() to return false conditionally
BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>() {
private static final long serialVersionUID = -6903933921423432194L;
@Override
public boolean offer(Runnable e) {
// Offer it to the queue if there is 0 items already queued, else
// return false so the TPE will add another thread. If we return false
// and max threads have been reached then the RejectedExecutionHandler
// will be called which will do the put into the queue.
if (size() == 0) {
return super.offer(e);
} else {
return false;
}
}
};
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(1 /*core*/, 50 /*max*/,
60 /*secs*/, TimeUnit.SECONDS, queue);
threadPool.setRejectedExecutionHandler(new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
try {
// This does the actual put into the queue. Once the max threads
// have been reached, the tasks will then queue up.
executor.getQueue().put(r);
// we do this after the put() to stop race conditions
if (executor.isShutdown()) {
throw new RejectedExecutionException(
"Task " + r + " rejected from " + e);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
}
}
});
इस तंत्र के साथ, जब मैं कतार में कार्य प्रस्तुत करता हूं, तो ThreadPoolExecutor
:
- प्रारंभ में कोर आकार तक थ्रेड्स की संख्या को स्केल करें (यहां 1)।
- इसे कतार में चढ़ाएं। यदि कतार खाली है, तो इसे मौजूदा थ्रेड्स द्वारा संभाला जाएगा।
- यदि कतार में 1 या अधिक तत्व पहले से ही हैं, तो
offer(...)
वह झूठी हो जाएगी।
- यदि झूठी लौटा दी जाती है, तो पूल में थ्रेड्स की संख्या को तब तक स्केल करें जब तक वे अधिकतम संख्या (50) तक नहीं पहुंच जाते।
- यदि अधिकतम पर तो यह कॉल करता है
RejectedExecutionHandler
RejectedExecutionHandler
कतार में तो डालता कार्य फीफो क्रम में पहली उपलब्ध धागा द्वारा संसाधित किया जा करने के लिए।
यद्यपि मेरे ऊपर दिए गए उदाहरण कोड में, कतार अनबाउंड है, आप इसे एक बंधी हुई कतार के रूप में भी परिभाषित कर सकते हैं। उदाहरण के लिए, यदि आप 1000 की क्षमता जोड़ते हैं LinkedBlockingQueue
तो यह होगा:
- अधिकतम तक थ्रेड्स को स्केल करें
- तब तक कतारबद्ध रहें जब तक कि यह 1000 कार्यों से भरा न हो
- तब तक कॉलर को ब्लॉक करें जब तक कि कतार के लिए जगह उपलब्ध न हो जाए।
इसके अलावा, यदि आपको तत्कालीन समय offer(...)
में
उपयोग करने की आवश्यकता है, तो आप टाइमआउट के बजाय विधि का RejectedExecutionHandler
उपयोग कर सकते हैं ।offer(E, long, TimeUnit)
Long.MAX_VALUE
चेतावनी:
यदि आपको शटडाउन के बाद कार्यों को निष्पादक में जोड़ने की उम्मीद है , तो हो सकता है कि जब आप निष्पादक-सेवा बंद कर दी गई हो, तो आप RejectedExecutionException
हमारे रिवाज को फेंकने के बारे में और अधिक सचेत होना चाहते हैं RejectedExecutionHandler
। इसे इंगित करने के लिए @RaduToader को धन्यवाद।
संपादित करें:
इस उत्तर के लिए एक और ट्वीक टीपीई से पूछा जा सकता है कि क्या कोई निष्क्रिय थ्रेड्स हैं और यदि ऐसा है तो केवल आइटम को एनक्यू करें। आपको इसके लिए एक सच्चा वर्ग बनाना होगा और ourQueue.setThreadPoolExecutor(tpe);
उस पर विधि जोड़ना होगा।
तब आपकी offer(...)
विधि कुछ इस तरह दिख सकती है:
- यह देखने के लिए जांचें कि क्या
tpe.getPoolSize() == tpe.getMaximumPoolSize()
किस मामले में सिर्फ कॉल करना है super.offer(...)
।
- और अगर
tpe.getPoolSize() > tpe.getActiveCount()
तब से कॉल करें, तो super.offer(...)
लगता है कि बेकार धागे हैं।
- अन्यथा
false
एक और धागा कांटा लौटाएं ।
शायद यह:
int poolSize = tpe.getPoolSize();
int maximumPoolSize = tpe.getMaximumPoolSize();
if (poolSize >= maximumPoolSize || poolSize > tpe.getActiveCount()) {
return super.offer(e);
} else {
return false;
}
ध्यान दें कि TPE पर प्राप्त तरीके महंगे हैं क्योंकि वे volatile
खेतों तक पहुँचते हैं या (मामले में getActiveCount()
) TPE को लॉक करते हैं और थ्रेड-लिस्ट चलते हैं। इसके अलावा, यहां दौड़ की स्थितियां भी हैं जिनके कारण कोई कार्य अनुचित तरीके से किया जा सकता है या जब कोई थ्रेड थ्रेड होता है तो एक अन्य थ्रेड कांटा जाता है।