ఎగ్జిక్యూటర్స్ క్లాస్ యొక్క కొత్త ఫిక్స్డ్థ్రెడ్పూల్ పద్ధతి నిర్ణీత సంఖ్యలో థ్రెడ్లతో ఎగ్జిక్యూటర్ సర్వీస్ను సృష్టిస్తుంది . newSingleThreadExecutor పద్ధతి వలె కాకుండా , పూల్లో మనకు ఎన్ని థ్రెడ్లు కావాలో పేర్కొంటాము. హుడ్ కింద, కింది కోడ్ అంటారు:
new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());
కోర్పూల్సైజ్ ( ఎగ్జిక్యూటర్ సర్వీస్ ప్రారంభమైనప్పుడు సిద్ధంగా ఉండే (ప్రారంభమయ్యే) థ్రెడ్ల సంఖ్య ) మరియు గరిష్ట పూల్సైజ్ ( ఎగ్జిక్యూటర్ సర్వీస్ సృష్టించగల గరిష్ట థ్రెడ్ల సంఖ్య ) పారామితులు ఒకే విలువను అందుకుంటాయి — కొత్త ఫిక్స్డ్థ్రెడ్పూల్ (nThreads) కి పంపబడిన థ్రెడ్ల సంఖ్య ) . మరియు మేము ThreadFactory యొక్క మా స్వంత అమలును సరిగ్గా అదే విధంగా పాస్ చేయవచ్చు .
సరే, మనకు అలాంటి ExecutorService ఎందుకు అవసరమో చూద్దాం .
స్థిర సంఖ్య (n) థ్రెడ్లతో ఎగ్జిక్యూటర్ సర్వీస్ యొక్క లాజిక్ ఇక్కడ ఉంది :
- ప్రాసెసింగ్ టాస్క్ల కోసం గరిష్టంగా n థ్రెడ్లు సక్రియంగా ఉంటాయి.
- n కంటే ఎక్కువ టాస్క్లు సమర్పించబడితే, థ్రెడ్లు ఖాళీ అయ్యే వరకు అవి క్యూలో ఉంచబడతాయి.
- థ్రెడ్లలో ఒకటి విఫలమైతే మరియు ఆగిపోయినట్లయితే, దాని స్థానంలో కొత్త థ్రెడ్ సృష్టించబడుతుంది.
- పూల్ మూసివేయబడే వరకు పూల్లోని ఏదైనా థ్రెడ్ సక్రియంగా ఉంటుంది.
ఉదాహరణగా, విమానాశ్రయంలో భద్రత కోసం వేచి ఉండడాన్ని ఊహించుకోండి. భద్రతా తనిఖీకి ముందు, పని చేసే అన్ని చెక్పోస్టుల మధ్య ప్రయాణీకులు పంపిణీ చేయబడే వరకు అందరూ ఒకే వరుసలో నిలబడతారు. చెక్పాయింట్లలో ఒకదానిలో ఆలస్యం జరిగితే, మొదటిది ఉచితం అయ్యే వరకు క్యూ రెండవది మాత్రమే ప్రాసెస్ చేయబడుతుంది. మరియు ఒక చెక్పాయింట్ పూర్తిగా మూసివేయబడితే, దాని స్థానంలో మరొక చెక్పాయింట్ తెరవబడుతుంది మరియు ప్రయాణీకులు రెండు చెక్పాయింట్ల ద్వారా ప్రాసెస్ చేయబడటం కొనసాగుతుంది.
పరిస్థితులు అనువైనవి అయినప్పటికీ — వాగ్దానం చేయబడిన n థ్రెడ్లు స్థిరంగా పని చేస్తాయి మరియు లోపంతో ముగిసే థ్రెడ్లు ఎల్లప్పుడూ భర్తీ చేయబడతాయని మేము వెంటనే గమనిస్తాము (నిజమైన విమానాశ్రయంలో పరిమిత వనరులు సాధించడం అసాధ్యం) — సిస్టమ్లో ఇంకా అనేకం ఉన్నాయి అసహ్యకరమైన లక్షణాలు, ఎందుకంటే ఎట్టి పరిస్థితుల్లోనూ ఎక్కువ థ్రెడ్లు ఉండవు, థ్రెడ్లు టాస్క్లను ప్రాసెస్ చేయగల దానికంటే క్యూ వేగంగా పెరిగినప్పటికీ.
నిర్ణీత సంఖ్యలో థ్రెడ్లతో ఎగ్జిక్యూటర్సర్వీస్ ఎలా పనిచేస్తుందనే దానిపై ఆచరణాత్మక అవగాహనను పొందాలని నేను సూచిస్తున్నాను . Runnableని అమలు చేసే తరగతిని సృష్టిద్దాం . ఈ తరగతి యొక్క ఆబ్జెక్ట్లు ఎగ్జిక్యూటర్ సర్వీస్ కోసం మా టాస్క్లను సూచిస్తాయి .
public class Task implements Runnable {
int taskNumber;
public Task(int taskNumber) {
this.taskNumber = taskNumber;
}
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Processed user request #" + taskNumber + " on thread " + Thread.currentThread().getName());
}
}
రన్ () పద్ధతిలో, మేము థ్రెడ్ను 2 సెకన్ల పాటు బ్లాక్ చేస్తాము, కొంత పనిభారాన్ని అనుకరిస్తాము, ఆపై ప్రస్తుత టాస్క్ నంబర్ మరియు టాస్క్ను అమలు చేస్తున్న థ్రెడ్ పేరును ప్రదర్శిస్తాము.
ExecutorService executorService = Executors.newFixedThreadPool(3);
for (int i = 0; i < 30; i++) {
executorService.execute(new Task(i));
}
executorService.shutdown();
ప్రారంభించడానికి, ప్రధాన పద్ధతిలో, మేము ExecutorService ని సృష్టించి , అమలు కోసం 30 టాస్క్లను సమర్పించాము.
1 ప్రాసెస్ చేయబడింది పూల్-1-థ్రెడ్-1 థ్రెడ్లో
వినియోగదారు అభ్యర్థన #0 ప్రాసెస్ చేయబడింది పూల్-1-థ్రెడ్-3 థ్రెడ్లో
వినియోగదారు అభ్యర్థన #2 ప్రాసెస్ చేయబడింది 1-థ్రెడ్-3 థ్రెడ్
పూల్-1-థ్రెడ్-2 థ్రెడ్లో వినియోగదారు అభ్యర్థన #3
ప్రాసెస్ చేయబడింది పూల్-1-థ్రెడ్-1 థ్రెడ్లో వినియోగదారు
అభ్యర్థన #4 ప్రాసెస్ చేయబడింది
పూల్-1-థ్రెడ్-3 థ్రెడ్లో #6
అభ్యర్థన ప్రాసెస్ చేయబడిన వినియోగదారు అభ్యర్థన #7 పూల్-1-థ్రెడ్-2 థ్రెడ్లో
ప్రాసెస్ చేయబడిన వినియోగదారు అభ్యర్థన #10 పూల్-1-థ్రెడ్-3 థ్రెడ్లో
ప్రాసెస్ చేయబడిన వినియోగదారు అభ్యర్థన #9 పూల్-1లో థ్రెడ్-1 థ్రెడ్
పూల్-1-థ్రెడ్-2 థ్రెడ్లో వినియోగదారు అభ్యర్థన #11 ప్రాసెస్ చేయబడింది
పూల్-1-థ్రెడ్-3 థ్రెడ్లో వినియోగదారు అభ్యర్థన #12 ప్రాసెస్ చేయబడింది
పూల్-1-థ్రెడ్-2 థ్రెడ్లో వినియోగదారు అభ్యర్థన #14 ప్రాసెస్ చేయబడింది
పూల్-1-థ్రెడ్-1 థ్రెడ్లో వినియోగదారు అభ్యర్థన #13
ప్రాసెస్ చేయబడిన వినియోగదారు అభ్యర్థన #15 పూల్-1-థ్రెడ్-3 థ్రెడ్లో ప్రాసెస్
చేయబడిన వినియోగదారు అభ్యర్థన #16 పూల్లో ప్రాసెస్ చేయబడింది- 1-థ్రెడ్-2 థ్రెడ్
పూల్-1-థ్రెడ్-1 థ్రెడ్లో వినియోగదారు అభ్యర్థన #17
ప్రాసెస్
చేయబడింది
. పూల్-1-థ్రెడ్-1 థ్రెడ్లో #20
అభ్యర్థన ప్రాసెస్ చేయబడిన వినియోగదారు అభ్యర్థన #21 పూల్-1-థ్రెడ్-3 థ్రెడ్లో
ప్రాసెస్ చేయబడిన వినియోగదారు అభ్యర్థన #22 పూల్-1-థ్రెడ్-2 థ్రెడ్లో ప్రాసెస్
చేయబడిన వినియోగదారు అభ్యర్థన #23 పూల్-1-పై ప్రాసెస్ చేయబడింది థ్రెడ్-1 థ్రెడ్
పూల్-1-థ్రెడ్-2 థ్రెడ్లో వినియోగదారు అభ్యర్థన #25 ప్రాసెస్ చేయబడింది
పూల్-1-థ్రెడ్-3 థ్రెడ్లో వినియోగదారు అభ్యర్థన #24 ప్రాసెస్ చేయబడింది
పూల్-1-థ్రెడ్-1 థ్రెడ్లో
#26 వినియోగదారు అభ్యర్థన ప్రాసెస్
చేయబడింది
1-థ్రెడ్-1 థ్రెడ్
కన్సోల్ అవుట్పుట్ మునుపటి టాస్క్ ద్వారా విడుదల చేయబడిన తర్వాత వివిధ థ్రెడ్లపై టాస్క్లు ఎలా అమలు చేయబడతాయో చూపిస్తుంది.
ఇప్పుడు మేము టాస్క్ల సంఖ్యను 100కి పెంచుతాము మరియు 100 టాస్క్లను సమర్పించిన తర్వాత, మేము waiitTermination(11, SECONDS) పద్ధతిని పిలుస్తాము . మేము ఆర్గ్యుమెంట్లుగా నంబర్ మరియు టైమ్ యూనిట్ని పాస్ చేస్తాము. ఈ పద్ధతి ప్రధాన థ్రెడ్ను 11 సెకన్ల పాటు బ్లాక్ చేస్తుంది. అన్ని పనులు పూర్తయ్యే వరకు వేచి ఉండకుండా ఎగ్జిక్యూటర్ సర్వీస్ను షట్ డౌన్ చేయమని ఒత్తిడి చేయడానికి మేము shutdownNow() అని పిలుస్తాము .
ExecutorService executorService = Executors.newFixedThreadPool(3);
for (int i = 0; i < 100; i++) {
executorService.execute(new Task(i));
}
executorService.awaitTermination(11, SECONDS);
executorService.shutdownNow();
System.out.println(executorService);
ముగింపులో, మేము ఎగ్జిక్యూటర్ సర్వీస్ స్థితి గురించి సమాచారాన్ని ప్రదర్శిస్తాము .
మనకు లభించే కన్సోల్ అవుట్పుట్ ఇక్కడ ఉంది:
పూల్-1-థ్రెడ్-3 థ్రెడ్లో
వినియోగదారు అభ్యర్థన #2 ప్రాసెస్ చేయబడింది పూల్-1-థ్రెడ్-2 థ్రెడ్లో
వినియోగదారు అభ్యర్థన #1 ప్రాసెస్ చేయబడింది 1-థ్రెడ్-3 థ్రెడ్
పూల్-1-థ్రెడ్-2 థ్రెడ్లో వినియోగదారు అభ్యర్థన #5 ప్రాసెస్
చేయబడింది పూల్-1-థ్రెడ్-1 థ్రెడ్లో
వినియోగదారు అభ్యర్థన #3 ప్రాసెస్ చేయబడింది పూల్-1-థ్రెడ్-3 థ్రెడ్లో
వినియోగదారు అభ్యర్థన #6 ప్రాసెస్ చేయబడింది పూల్-1-థ్రెడ్-2 థ్రెడ్లో #7
అభ్యర్థన ప్రాసెస్ చేయబడిన వినియోగదారు అభ్యర్థన #8 పూల్-1-థ్రెడ్-1 థ్రెడ్లో
ప్రాసెస్ చేయబడిన వినియోగదారు అభ్యర్థన #9 పూల్-1-థ్రెడ్-3 థ్రెడ్లో
ప్రాసెస్ చేయబడిన వినియోగదారు అభ్యర్థన #11 పూల్-1-పై ప్రాసెస్ చేయబడింది థ్రెడ్-1 థ్రెడ్
పూల్-1-థ్రెడ్-2 థ్రెడ్లో వినియోగదారు అభ్యర్థన #10 ప్రాసెస్ చేయబడింది
పూల్-1-థ్రెడ్-1 థ్రెడ్లో వినియోగదారు అభ్యర్థన #13 ప్రాసెస్ చేయబడింది
పూల్-1-థ్రెడ్-2 థ్రెడ్లో వినియోగదారు అభ్యర్థన #14 ప్రాసెస్ చేయబడింది
పూల్-1-థ్రెడ్-3 థ్రెడ్లో వినియోగదారు అభ్యర్థన #12
java.util.concurrent.ThreadPoolExecutor@452b3a41[షట్ డౌన్, పూల్ పరిమాణం = 3, యాక్టివ్ థ్రెడ్లు = 3 , క్యూలో ఉన్న పనులు = 0, పూర్తయిన పనులు = 15]
పూల్-1-థ్రెడ్-3 థ్రెడ్లో వినియోగదారు అభ్యర్థన #17 ప్రాసెస్ చేయబడిన వినియోగదారు అభ్యర్థన
#15 పూల్-1-థ్రెడ్-1 థ్రెడ్లో
ప్రాసెస్ చేయబడిన వినియోగదారు అభ్యర్థన #16 పూల్-1-థ్రెడ్లో -2 థ్రెడ్
దీని తర్వాత 3 యాక్టివ్ టాస్క్ల నుండి స్లీప్ మెథడ్స్ ద్వారా విసిరిన 3 అంతరాయ మినహాయింపులు ఉన్నాయి.
ప్రోగ్రామ్ ముగిసినప్పుడు, 15 టాస్క్లు పూర్తయినట్లు మనం చూడవచ్చు, అయితే పూల్ ఇప్పటికీ 3 యాక్టివ్ థ్రెడ్లను కలిగి ఉంది, అవి వాటి టాస్క్లను అమలు చేయడం పూర్తి కాలేదు. ఈ మూడు థ్రెడ్లలో అంతరాయ () పద్ధతిని పిలుస్తారు, అంటే పని పూర్తవుతుంది, కానీ మా విషయంలో, నిద్ర పద్ధతి అంతరాయ మినహాయింపును విసురుతుంది . shutdownNow() పద్ధతిని పిలిచిన తర్వాత, టాస్క్ క్యూ క్లియర్ చేయబడిందని కూడా మనం చూస్తాము .
కాబట్టి పూల్లో నిర్ణీత సంఖ్యలో థ్రెడ్లతో ExecutorServiceని ఉపయోగిస్తున్నప్పుడు , అది ఎలా పని చేస్తుందో గుర్తుంచుకోండి. తెలిసిన స్థిరమైన లోడ్ ఉన్న పనులకు ఈ రకం అనుకూలంగా ఉంటుంది.
ఇక్కడ మరొక ఆసక్తికరమైన ప్రశ్న ఉంది: మీరు ఒకే థ్రెడ్ కోసం ఎగ్జిక్యూటర్ని ఉపయోగించాల్సి వస్తే, మీరు ఏ పద్ధతికి కాల్ చేయాలి? newSingleThreadExecutor() లేదా newFixedThreadPool(1) ?
ఇద్దరు కార్యనిర్వాహకులు సమానమైన ప్రవర్తనను కలిగి ఉంటారు. ఒకే తేడా ఏమిటంటే, newSingleThreadExecutor() పద్ధతి అదనపు థ్రెడ్లను ఉపయోగించడానికి తర్వాత మళ్లీ కాన్ఫిగర్ చేయలేని ఎగ్జిక్యూటర్ని అందిస్తుంది.
GO TO FULL VERSION