تعارف
تنهن ڪري، اسان ڄاڻون ٿا ته جاوا ۾ موضوع آهن، جن بابت توهان جائزو ۾ پڙهي سگهو ٿا " توهان جاوا کي هڪ موضوع سان خراب نه ٿا ڪري سگهو: حصو I - موضوع ". اچو ته نموني ڪوڊ کي ٻيهر ڏسو:public static void main(String []args) throws Exception {
Runnable task = () -> {
System.out.println("Task executed");
};
Thread thread = new Thread(task);
thread.start();
}
جيئن ته اسان ڏسي سگهون ٿا، ڪم شروع ڪرڻ لاء ڪوڊ ڪافي معياري آهي، پر هر نئين لانچ لاء اسان کي ان کي ٻيهر ڪرڻو پوندو. هڪ حل اهو آهي ته ان کي الڳ طريقي سان منتقل ڪيو وڃي، مثال طور execute(Runnable runnable)
. پر جاوا ڊولپر اڳ ۾ ئي اسان جي باري ۾ پريشان آهن ۽ هڪ انٽرفيس سان گڏ ايندا آهن Executor
:
public static void main(String []args) throws Exception {
Runnable task = () -> System.out.println("Task executed");
Executor executor = (runnable) -> {
new Thread(runnable).start();
};
executor.execute(task);
}
جئين توهان ڏسي سگهو ٿا، ڪوڊ وڌيڪ جامع ٿي چڪو آهي ۽ اسان کي اجازت ڏني وئي آهي ته صرف ڪوڊ لکڻ لاء ان کي Runnable
هڪ سلسلي ۾ هلائڻ لاء. عظيم، آهي نه؟ پر هي صرف شروعات آهي:
https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Executor.html
Executor
هڪ نسلي انٽرفيس آهي ExecutorService
. هن انٽرفيس جو JavaDoc چوي ٿو ته ExecutorService
اهو هڪ خاص Executor
'a' جو بيان آهي جيڪو ڪم 'a' کي روڪڻ جا طريقا مهيا ڪري ٿو Executor
۽ توهان کي اجازت ڏئي ٿو ته توهان java.util.concurrent.Future
عملدرآمد جي ترقي کي ٽريڪ ڪرڻ لاءِ. اڳي، " توهان موضوع سان جاوا خراب نه ٿا ڪري سگهو: حصو IV - ڪالبل، مستقبل ۽ دوست، " اسان مختصر طور تي جائزو ورتو Future
. جيڪڏهن توهان وساريو يا نه پڙهيو آهي، مان توهان کي صلاح ڏيان ٿو توهان جي ياداشت کي تازو ڪريو؛) JavaDoc ۾ ٻيو ڇا دلچسپ آهي؟ اهو ته اسان وٽ هڪ خاص ڪارخانو آهي java.util.concurrent.Executors
جيڪو اسان کي اجازت ڏئي ٿو ته انهن تي عمل درآمد ڪري سگهون جيڪي ڊفالٽ طور موجود آهن ExecutorService
.
ايگزيڪيوٽر سروس
اچو ته ٻيهر ياد رکون. اسان کيExecutor
ٿريڊ ۾ ڪنهن خاص ڪم کي ايگزيڪيوٽو (يعني execute) ڪرڻو پوندو آهي، جڏهن ته ٿريڊ ٺاهڻ جو عمل اسان کان لڪيل هوندو آهي. اسان وٽ ExecutorService
ھڪڙو خاص آھي Executor
جيڪو عمل جي ترقي کي منظم ڪرڻ لاء صلاحيتن جو ھڪڙو سيٽ آھي. ۽ اسان وٽ هڪ ڪارخانو آهي Executors
جيڪو توهان کي ٺاهڻ جي اجازت ڏئي ٿو ExecutorService
. اچو ته هاڻي پاڻ ڪريون:
public static void main(String[] args) throws ExecutionException, InterruptedException {
Callable<String> task = () -> Thread.currentThread().getName();
ExecutorService service = Executors.newFixedThreadPool(2);
for (int i = 0; i < 5; i++) {
Future result = service.submit(task);
System.out.println(result.get());
}
service.shutdown();
}
جيئن اسان ڏسي سگهون ٿا، اسان هڪ مقرر ٿيل ٿريڊ پول ( Fixed Thread Pool
) جي سائيز 2 جي وضاحت ڪئي آهي. جنهن کان پوءِ اسان هڪ هڪ ڪري پول ڏانهن ڪم موڪليندا آهيون. هر ڪم هڪ اسٽرنگ ( String
) ڏي ٿو جنهن ۾ سلسلي جو نالو ( currentThread().getName()
). اهو ضروري آهي ته آخر ۾ بند ڪيو وڃي ExecutorService
، ڇاڪاڻ ته ٻي صورت ۾ اسان جو پروگرام نه نڪرندو. فيڪٽري ۾ Executors
ٻيا فيڪٽري طريقا موجود آهن . مثال طور، اسان صرف ھڪڙي سلسلي جو ھڪڙو تلاءُ ٺاھي سگھون ٿا - newSingleThreadExecutor
يا ڪيشنگ سان ھڪڙو تلاءُ newCachedThreadPool
، جتي 1 منٽ لاءِ بيڪار رھڻ جي صورت ۾ ٿريڊز کي پول مان ڪڍيو ويندو. حقيقت ۾، انهن جي پويان هڪ بلاڪنگ قطارExecutorService
آهي جنهن ۾ ڪم رکيا ويا آهن ۽ جن مان اهي ڪم انجام ڏنا ويا آهن. قطارن کي بلاڪ ڪرڻ بابت وڌيڪ معلومات وڊيو ۾ ڏسي سگھجي ٿو " بلاڪ ڪرڻ قطار - مجموعو # 5 - ترقي يافته جاوا ". توھان پڻ پڙھي سگھوٿا جائزو ” سمورو پيڪيج جون قطارون بلاڪ ڪرڻ ” ۽ سوال جو جواب ” ڪڏھن LinkedBlockingQueue کي ArrayBlockingQueue تي ترجيح ڏيو ؟ سپر آسان - (قطار کي بلاڪ ڪرڻ) ھڪڙي سلسلي کي بلاڪ ڪري ٿو، ٻن صورتن ۾: BlockingQueue
- هڪ موضوع هڪ خالي قطار مان عناصر حاصل ڪرڻ جي ڪوشش ڪري رهيو آهي
- ٿريڊ ڪوشش ڪري رهي آهي عناصرن کي مڪمل قطار ۾
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
يا
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
جيئن ته اسان ڏسي سگهون ٿا، عملدرآمد فيڪٽري طريقن جي اندر ٺاهيا ويا آهن ExecutorService
. ۽ اهو بنيادي طور تي آهي ThreadPoolExecutor
. صرف اهي خاصيتون جيڪي ڪم تي اثر انداز ڪن ٿا.
https://en.wikipedia.org/wiki/Thread_pool#/media/File:Thread_pool.svg
ThreadPoolExecutor
جيئن اسان اڳ ۾ ڏٺو، ڪارخاني جا طريقا اندرThreadPoolExecutor
، . ڪارڪردگي متاثر ٿئي ٿي ته ڪهڙا قدر گذري ويا آهن وڌ ۾ وڌ ۽ گهٽ ۾ گهٽ سلسلا، انهي سان گڏ ڪهڙي قطار استعمال ٿئي ٿي. ۽ انٽرفيس جو ڪو به عمل استعمال ڪري سگھجي ٿو java.util.concurrent.BlockingQueue
. 'ahs' جي ڳالهائيندي ThreadPoolExecutor
، اهو آپريشن دوران دلچسپ خاصيتن کي ڌيان ڏيڻ جي قابل آهي. مثال طور، توهان ڪمن کي نه موڪلي سگهو ٿا ThreadPoolExecutor
جيڪڏهن اتي ڪا جاء ناهي:
public static void main(String[] args) throws ExecutionException, InterruptedException {
int threadBound = 2;
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(0, threadBound,
0L, TimeUnit.SECONDS, new SynchronousQueue<>());
Callable<String> task = () -> {
Thread.sleep(1000);
return Thread.currentThread().getName();
};
for (int i = 0; i < threadBound + 1; i++) {
threadPoolExecutor.submit(task);
}
threadPoolExecutor.shutdown();
}
هي ڪوڊ هڪ غلطي سان ناڪام ٿيندو جهڙوڪ:
Task java.util.concurrent.FutureTask@7cca494b rejected from java.util.concurrent.ThreadPoolExecutor@7ba4f24f[Running, pool size = 2, active threads = 2, queued tasks = 0, completed tasks = 0]
اهو آهي، task
توهان جمع نه ٿا ڪري سگهو، ڇاڪاڻ ته SynchronousQueue
اهو اهڙي طرح ٺهيل آهي ته اهو اصل ۾ هڪ عنصر تي مشتمل آهي ۽ توهان کي وڌيڪ رکڻ جي اجازت ناهي. جيئن اسان ڏسي سگهون ٿا، queued tasks
هتي 0 آهي، ۽ هن ۾ ڪجهه به عجيب ناهي، ڇاڪاڻ ته هي مخصوص آهي SynchronousQueue
- حقيقت ۾، اهو 1 عنصر جي قطار آهي، جيڪو هميشه خالي هوندو آهي. (!) جڏهن هڪ ڌاڳو هڪ عنصر کي قطار ۾ رکي ٿو، اهو انتظار ڪندو جيستائين ٻيو ٿريڊ ان عنصر کي قطار مان وٺي. تنهن ڪري، اسان ان سان تبديل ڪري سگهون ٿا new LinkedBlockingQueue<>(1)
۽ غلطي ۾ ڇا اشارو ڪيو ويندو تبديل ٿي ويندو queued tasks = 1
. ڇاڪاڻ ته قطار صرف 1 عنصر آهي، پوء اسان ٻئي کي شامل نٿا ڪري سگھون. ۽ اسان هن تي گر ڪنداسين. قطار جي موضوع کي جاري رکڻ، اهو سمجهڻ جي قابل آهي ته ڪلاس ۾ ThreadPoolExecutor
قطار جي خدمت ڪرڻ لاء اضافي طريقا آهن. مثال طور، طريقو threadPoolExecutor.purge()
سڀني منسوخ ٿيل ڪمن کي قطار مان ڪڍي ڇڏيندو قطار ۾ جاء خالي ڪرڻ لاء. قطار سان لاڳاپيل هڪ ٻي دلچسپ خصوصيت ناقابل قبول ٽاسڪ سنڀاليندڙ آهي:
public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1,
0L, TimeUnit.SECONDS, new SynchronousQueue());
Callable<String> task = () -> Thread.currentThread().getName();
threadPoolExecutor.setRejectedExecutionHandler((runnable, executor) -> System.out.println("Rejected"));
for (int i = 0; i < 5; i++) {
threadPoolExecutor.submit(task);
}
threadPoolExecutor.shutdown();
}
مثال طور، هينڊلر صرف هڪ لفظ پرنٽ ڪري ٿو Rejected
هر هڪ انڪار لاءِ هڪ ڪم کي قطار ۾ قبول ڪرڻ کان. آسان، آهي نه؟ ان کان سواء، ThreadPoolExecutor
هن وٽ هڪ دلچسپ وارث آهي - ScheduledThreadPoolExecutor
جيڪو آهي ScheduledExecutorService
. اهو هڪ ٽائمر تي ڪم انجام ڏيڻ جي صلاحيت ڏئي ٿو.
شيڊول ٿيل ايگزيڪيوٽر سروس
ExecutorService
ٽائيپ ScheduledExecutorService
توهان کي شيڊول مطابق ڪم هلائڻ جي اجازت ڏئي ٿي. اچو ته هڪ مثال ڏسو:
public static void main(String[] args) {
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(4);
Callable<String> task = () -> {
System.out.println(Thread.currentThread().getName());
return Thread.currentThread().getName();
};
scheduledExecutorService.schedule(task, 1, TimeUnit.MINUTES);
scheduledExecutorService.shutdown();
}
هتي سڀ ڪجھ سادو آهي. ٽاسڪ موڪليا ويا آهن، اسان کي "شيڊول ٿيل ڪم" ملي ٿو java.util.concurrent.ScheduledFuture
. ھيٺ ڏنل ڪيس شيڊول سان پڻ ڪارائتو ٿي سگھي ٿو:
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(4);
Runnable task = () -> {
System.out.println(Thread.currentThread().getName());
};
scheduledExecutorService.scheduleAtFixedRate(task, 1, 2, TimeUnit.SECONDS);
هتي اسان موڪليندا آهيون Runnable
ڪم کي مقرر ڪيل شرح (فڪسڊ ريٽ) تي هڪ خاص دير سان. انهي حالت ۾، 1 سيڪنڊ هر 2 سيڪنڊن کان پوء، ڪم تي عمل ڪرڻ شروع ڪريو. ھڪڙو ساڳيو اختيار آھي:
scheduledExecutorService.scheduleWithFixedDelay(task, 1, 2, TimeUnit.SECONDS);
پر هتي مختلف ڪمن جي عملدرآمد جي وچ ۾ ڏنل وقفي سان ڪم ڪيا ويندا آهن. اهو آهي، ڪم task
1 سيڪنڊ ۾ مڪمل ڪيو ويندو. اڳيون، جيترو جلدي مڪمل ڪيو ويندو، 2 سيڪنڊ گذري ويندا، ۽ پوء هڪ نئون ڪم شروع ڪيو ويندو. توھان ھن موضوع تي ھيٺيون مواد پڙھي سگھو ٿا:
- ٿريڊ پولز جو تعارف
- ٿريڊ پولز جو تعارف
- Java Multithreading Steeplechase: منسوخ ڪرڻ وارا ڪم عملدارن ۾
- پس منظر جي ڪمن لاءِ صحيح جاوا عملدار چونڊڻ
https://dzone.com/articles/diving-into-java-8s-newworkstealingpools
ڪم اسٽيلنگ پول
مٿي ذڪر ڪيل سلسلي جي تلاءَ کان علاوه، هڪ وڌيڪ آهي. توهان چئي سگهو ٿا ته هو ٿورو خاص آهي. ان جو نالو Work Stealing Pool آهي. مختصر ۾، Work Stealing ھڪڙو ڪم الورورٿم آھي جنھن ۾ idle threads ٻين موضوعن کان ڪم وٺڻ شروع ڪن ٿا يا عام قطار مان ڪم. اچو ته هڪ مثال ڏسو:public static void main(String[] args) {
Object lock = new Object();
ExecutorService executorService = Executors.newCachedThreadPool();
Callable<String> task = () -> {
System.out.println(Thread.currentThread().getName());
lock.wait(2000);
System.out.println("Finished");
return "result";
};
for (int i = 0; i < 5; i++) {
executorService.submit(task);
}
executorService.shutdown();
}
جيڪڏهن اسان هن ڪوڊ کي هلائيندا آهيون، ExecutorService
اهو 5 موضوع ٺاهيندو، ڇاڪاڻ ته هر موضوع اعتراض جي جڳهه تي انتظار جي قطار ۾ شامل ٿيندو lock
. اسان اڳ ۾ ئي ان تي مانيٽر ۽ لاڪ جي باري ۾ بحث ڪيو آهي " توهان هڪ موضوع سان جاوا خراب نٿا ڪري سگهو: حصو II - هم وقت سازي ." Executors.newCachedThreadPool
۽ هاڻي اسان ان کي تبديل ڪنداسين Executors.newWorkStealingPool()
. ڇا تبديلي ايندي؟ اسان ڏسنداسين ته اسان جا ڪم 5 موضوعن ۾ نه، پر گهٽ ۾ گهٽ آهن. ياد رهي ته cachedThreadPool
توهان هر ڪم لاءِ پنهنجو ٿريڊ ٺاهيو آهي؟ ڇاڪاڻ ته wait
ان سلسلي کي بلاڪ ڪيو، پر ايندڙ ڪمن کي انجام ڏيڻ چاهيندا هئا ۽ انهن لاء پول ۾ نوان موضوع ٺاهيا ويا. سلسلي جي صورت ۾ StealingPool
، اهي هميشه لاءِ بيڪار نه رهندا wait
، اهي پاڙيسري ڪمن کي انجام ڏيڻ شروع ڪندا. اهو ڪيئن ٻين موضوعن جي تلاء کان ايترو مختلف آهي WorkStealingPool
؟ ڇاڪاڻ ته واقعي هن جي اندر ۾ ڪجهه جادو آهي ForkJoinPool
:
public static ExecutorService newWorkStealingPool() {
return new ForkJoinPool
(Runtime.getRuntime().availableProcessors(),
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null, true);
}
اصل ۾ ھڪڙو وڌيڪ فرق آھي. ٿريڊس جيڪي ForkJoinPool
ڊفالٽ طور ٺاهيا ويا آهن اهي ڊيمون ٿريڊز آهن، جيئن ته ريگولر ذريعي ٺاهيل ٿريڊز جي برخلاف ThreadPool
. عام طور تي، اهو ياد رکڻ جي قابل آهي ڊيمن جي سلسلي بابت، ڇاڪاڻ ته ... مثال طور، CompletableFuture
ڊيمون موضوع پڻ استعمال ڪيا ويا آهن، جيڪڏهن توهان پنهنجي پنهنجي وضاحت نه ڪندا آهيو ThreadFactory
، جيڪي غير ڊيمون موضوع ٺاهي سگهندا. اهي اهڙيون حيرت انگيز آهن جيڪي توهان کي اڻڄاتل جاء تي انتظار ڪري سگهن ٿيون!)
فورڪ / جوائن پول
هن حصي ۾ اسان ساڳيءَ بابت ڳالهائينداسينForkJoinPool
(جنهن کي ڪانٽو/ جوائن فريم ورڪ پڻ سڏيو ويندو آهي) جيڪو رهي ٿو ”هوڊ هيٺ“ WorkStealingPool
. عام طور تي، فورڪ جوائن فريم ورڪ جاوا 1.7 ۾ ظاهر ٿيو. ۽ جيتوڻيڪ جاوا 11 اڳ ۾ ئي يارڊ ۾ آهي، اهو اڃا تائين ياد رکڻ جي قابل آهي. نه سڀ کان عام ڪم، پر ڪافي دلچسپ. انٽرنيٽ تي هن موضوع تي هڪ سٺو جائزو آهي: " فورڪ / جاوا 7 ۾ فريم ورڪ جوائن ڪريو ". Fork/JoinPool
هن جي ڪم ۾ اهڙي تصور سان هلندي آهي java.util.concurrent.RecursiveTask
. اتي پڻ ھڪڙو اينالاگ آھي - java.util.concurrent.RecursiveAction
. RecursiveActions نتيجو نه موٽائي. اهڙيءَ RecursiveTask
طرح سان ملندڙ جلندڙ Callable
۽ RecursiveAction
ملندڙ جلندڙ Runnable
. خير، نالو ڏسي، اسان کي ٻه اهم طريقا نظر اچن ٿا - fork
۽ join
. طريقو fork
هڪ الڳ سلسلي ۾ هڪ ڪم کي هم وقت سازي سان هلائي ٿو. ۽ طريقو join
توهان کي ڪم مڪمل ڪرڻ لاء انتظار ڪرڻ جي اجازت ڏئي ٿو. ان کي استعمال ڪرڻ جا ڪيترائي طريقا آهن: هي تصوير Alexey Shipilev جي رپورٽ مان هڪ سلائيڊ جو حصو آهي “ فورڪ/جوائن: عملدرآمد، استعمال، ڪارڪردگي .” ان کي واضح ڪرڻ لاءِ، ان جي رپورٽ ڏسڻ جي قابل آهي JEE CONF: “ فورڪ جوائن تي عملدرآمد جون خاصيتون .”
اختصار ڪرڻ
تنهن ڪري، اسان هتي آهيون، جائزو جي ايندڙ حصي کي ختم ڪري رهيا آهيون.Executor
اسان اهو معلوم ڪيو ته ڇا اسان پهريون ڀيرو ٿريڊز تي عمل ڪرڻ لاءِ آيا آهيون . پوءِ اسان ان خيال کي جاري رکڻ جو فيصلو ڪيو ۽ ان سان گڏ آياسين ExecutorService
. ExecutorService
توهان کي اجازت ڏئي ٿو ته ڪمن کي موڪلڻ لاءِ استعمال ڪندي submit
۽ invoke
انهي سان گڏ ان کي بند ڪري سروس جو انتظام ڪريو. ڇاڪاڻ ته ExecutorService
'اسان کي لاڳو ڪرڻ جي ضرورت آهي، اسان فيڪٽري طريقن سان هڪ ڪلاس لکيو ۽ ان کي سڏيو Executors
. اهو توهان کي ٿريڊ پول ٺاهڻ جي اجازت ڏئي ٿو ThreadPoolExecutor
. ساڳي ئي وقت ۾، اهڙا ٿريڊ پول آهن جيڪي توهان کي اجازت ڏين ٿا ته عمل لاءِ شيڊول بيان ڪن، پر WorkStealingPool
لڪائي ٿو ForkJoinPool
. مون کي اميد آهي ته جيڪو مٿي لکيو ويو هو صرف توهان لاء دلچسپ نه هو، پر سمجھڻ وارو) مان هميشه خوش آهيان مشورا ۽ رايا حاصل ڪرڻ لاء. #وياچسلاو
GO TO FULL VERSION