تعارف
تنهن ڪري، اسان ڄاڻون ٿا ته جاوا موضوع آهن. توھان ان جي باري ۾ پڙھي سگھوٿا جائزي ۾ عنوان سان گڏ بهتر: جاوا ۽ ٿريڊ ڪلاس. حصو 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
جيڪو بند ڪرڻ جا طريقا مهيا ڪري ٿو Executor
. اهو پڻ ممڪن بڻائي ٿو ته هڪ حاصل java.util.concurrent.Future
ڪرڻ جي عمل کي ٽريڪ ڪرڻ لاء. اڳي، بهتر گڏ ۾: جاوا ۽ ٿريڊ ڪلاس. حصو IV - سڏڻ لائق، مستقبل، ۽ دوست
، اسان مختصر طور جي صلاحيتن جو جائزو ورتو Future
. جيڪڏھن توھان وساريو يا ڪڏھن به نه پڙھو، مان صلاح ڏيان ٿو ته توھان پنھنجي يادگيري کي تازو ڪريو؛) جاواڊڪ ٻيو ڇا ٿو چوي؟ اهو اسان کي ٻڌائي ٿو ته اسان وٽ هڪ خاص 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();
}
توھان ڏسي سگھو ٿا ته اسان ھڪ مقرر ٿيل ٿريڊ پول بيان ڪيو آھي جنھن جي سائيز 2 آھي. پوءِ اسان ھڪڙي ھڪڙي پول ڏانھن ڪم جمع ڪريون ٿا. هر ڪم هڪ ڏي ٿو String
جنهن ۾ سلسلي جو نالو شامل آهي ( currentThread().GetName()
). ExecutorService
اهو ضروري آهي ته آخر ۾ بند ڪيو وڃي ، ڇاڪاڻ ته ٻي صورت ۾ اسان جو پروگرام ختم نه ٿيندو. ڪارخاني Executors
۾ اضافي فيڪٽري طريقا آهن. مثال طور، اسان ھڪڙو تلاءُ ٺاھي سگھون ٿا جنھن ۾ صرف ھڪڙي سلسلي ( newSingleThreadExecutor
) يا ھڪڙو تلاءُ شامل آھي جنھن ۾ ڪيش ( newCachedThreadPool
) شامل آھي جنھن مان 1 منٽ لاءِ بيڪار رھڻ کان پوءِ ھٽايو وڃي ٿو. حقيقت ۾، اهي ExecutorService
هڪ بلاڪنگ قطار جي پٺڀرائي آهن ، جن ۾ ڪم رکيا ويا آهن ۽ جن مان ڪم سرانجام ڏنا ويا آهن. قطارن کي بلاڪ ڪرڻ بابت وڌيڪ معلومات هن وڊيو
۾ ملي سگهي ٿي . توھان پڻ پڙھي سگھوٿا ھي جائزو BlockingQueue بابت
. ۽ پڙتال ڪريو سوال جو جواب "ڪڏهن 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
انٽرفيس جي ڪنهن به عمل کي استعمال ڪري سگهجي ٿو. جي ڳالهائيندي 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
. اهو هڪ ٽائمر جي بنياد تي ڪم انجام ڏيڻ جي صلاحيت ڏئي ٿو.
شيڊول ٿيل ايگزيڪيوٽر سروس
ScheduledExecutorService
(جيڪو هڪ قسم جو آهي ExecutorService
) اسان کي شيڊول تي ڪم هلائڻ جي اجازت ڏئي ٿي. اچو ته هڪ مثال ڏسو:
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
ڪم اسٽيلنگ پول
مٿين سلسلي جي تلاء کان علاوه، ھڪڙو وڌيڪ آھي. اسان ايمانداري سان چئي سگهون ٿا ته اهو ٿورو خاص آهي. اهو هڪ ڪم چوري تلاء سڏيو ويندو آهي. مختصر ۾، ڪم چوري هڪ الگورٿم آهي جنهن ۾ بيڪار موضوع ٻين موضوعن کان ڪم وٺڻ شروع ڪندا آهن يا گڏيل قطار مان ڪم. اچو ته هڪ مثال ڏسو: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 ٿريڊ ٺاهيندو، ڇاڪاڻ ته هر ٿريڊ کي لاڪ اعتراض جي انتظار جي قطار ۾ رکيو ويندو. اسان اڳ ۾ ئي مانيٽر ۽ لاڪ کي بهتر ۾ گڏ ڪيو آهي: جاوا ۽ ٿريڊ ڪلاس. حصو II - هم وقت سازي
. هاڻي اچو ته Executors.newCachedThreadPool()
ان سان تبديل ڪريون Executors.newWorkStealingPool()
. ڇا تبديلي ايندي؟ اسان ڏسنداسين ته اسان جا ڪم 5 کان گهٽ ٿريڊن تي عمل ڪيا ويا آهن. ياد رکو ته CachedThreadPool
هر ڪم لاء هڪ موضوع ٺاهي ٿو؟ اهو ان ڪري جو wait()
ان سلسلي کي بلاڪ ڪيو ويو، بعد ۾ ڪم مڪمل ٿيڻ چاهين ٿا، ۽ پول ۾ انهن لاءِ نوان موضوع ٺاهيا ويا. چوري جي تلاءَ سان، ڌاڙا هميشه لاءِ بيڪار نه رهندا آهن. اهي پنهنجن پاڙيسرين جي ڪمن کي انجام ڏيڻ شروع ڪن ٿا. ڇا ٿو ڪري ٿو WorkStealingPool
ٻين موضوعن جي تلاء کان ايترو مختلف؟ حقيقت اها آهي ته جادو ForkJoinPool
ان جي اندر رهي ٿو:
public static ExecutorService newWorkStealingPool() {
return new ForkJoinPool
(Runtime.getRuntime().availableProcessors(),
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null, true);
}
حقيقت ۾، اتي هڪ وڌيڪ فرق آهي. ڊفالٽ طور، هڪ لاءِ ٺاهيل ٿريڊس ForkJoinPool
ڊيمن ٿريڊز آهن، برعڪس آنڊنري ذريعي ٺاهيل ٿريڊز ThreadPool
. عام طور تي، توهان کي ياد رکڻ گهرجي ڊيمون ٿريڊز، ڇاڪاڻ ته، مثال طور، CompletableFuture
پڻ ڊيمون موضوعن کي استعمال ڪري ٿو، جيستائين توهان پنهنجو پاڻ کي بيان نه ڪيو ThreadFactory
جيڪو غير ڊيمون موضوع ٺاهي ٿو. اهي اهي تعجب آهن جيڪي اڻڄاتل هنڌن تي لڪي سگهن ٿيون! :)
ForkJoinPool
هن حصي ۾، اسان ٻيهر ڳالهائينداسينForkJoinPool
(جنهن کي فورڪ / جوائن فريم ورڪ پڻ سڏيو ويندو آهي)، جيڪو "هوڊ هيٺ" جي زندگي گذاريندو آهي WorkStealingPool
. عام طور تي، ڪانٽو/جوائن فريم ورڪ واپس جاوا 1.7 ۾ ظاهر ٿيو. ۽ جيتوڻيڪ جاوا 11 هٿ ۾ ويجهو آهي، اهو اڃا تائين ياد رکڻ جي قابل آهي. اهو سڀ کان وڌيڪ عام عمل نه آهي، پر اهو ڪافي دلچسپ آهي. ويب تي ان بابت هڪ سٺو جائزو آهي: جاوا فورڪ کي سمجھڻ- مثالن سان شامل ٿيڻ واري فريم ورڪ کي
. ForkJoinPool
تي دارومدار رکي ٿو java.util.concurrent.RecursiveTask
. اتي پڻ آهي java.util.concurrent.RecursiveAction
. RecursiveAction
نتيجو واپس نه ٿو ڏئي. اهڙيء طرح، RecursiveTask
سان ملندڙ جلندڙ آهي Callable
، ۽ RecursiveAction
هڪجهڙائي آهي unnable
. اسان ڏسي سگهون ٿا ته نالو ٻن اهم طريقن جا نالا شامل آهن: fork
۽ join
. اهو fork
طريقو هڪ الڳ موضوع تي ڪجهه ڪم کي هم وقت سازي سان شروع ڪري ٿو. ۽ join
طريقو توهان کي ڪم ڪرڻ لاء انتظار ڪرڻ جي اجازت ڏئي ٿو. بھترين سمجھ حاصل ڪرڻ لاءِ، توھان کي پڙھڻ گھرجي Imperative Programming کان فورڪ/Join to Parallel Streams in Java 8
.
خلاصو
خير، اهو جائزو جي هن حصي کي لپي ٿو. اسان اهو ڄاڻيو آهي تهExecutor
اصل ۾ ڌاڙن تي عمل ڪرڻ لاء ايجاد ڪئي وئي هئي. پوءِ جاوا جي تخليق ڪندڙن ان خيال کي جاري رکڻ جو فيصلو ڪيو ۽ ان سان گڏ آيا ExecutorService
. ExecutorService
اسان کي استعمال ڪرڻ لاءِ ڪم جمع ڪرڻ جي اجازت ڏئي ٿي submit()
۽ استعمال ڪندي invoke()
، ۽ پڻ خدمت کي بند ڪري. ڇاڪاڻ ته ExecutorService
عمل درآمد جي ضرورت آهي، انهن فيڪٽري طريقن سان هڪ ڪلاس لکيو ۽ ان کي سڏيو Executors
. اهو توهان کي ٿريڊ پول ٺاهڻ جي اجازت ڏئي ٿو ( ThreadPoolExecutor
). اضافي طور تي، اهڙا ٿريڊ پول آهن جيڪي پڻ اسان کي اجازت ڏين ٿا هڪ عملدرآمد شيڊول جي وضاحت ڪريو. ۽ ForkJoinPool
هڪ جي پويان لڪائي ٿو WorkStealingPool
. مون کي اميد آهي ته توهان کي مليو جيڪو مون مٿي لکيو آهي نه رڳو دلچسپ، پر سمجھڻ وارو پڻ :) مان توهان جي تجويز ۽ رايا ٻڌي هميشه خوش آهيان. گڏو گڏ بهتر: جاوا ۽ ٿريڊ ڪلاس. حصو I - عملدرآمد جا موضوع
گڏ ڪري بهتر: جاوا ۽ ٿريڊ ڪلاس. حصو II - هم وقت سازي
بهتر گڏجي: جاوا ۽ ٿريڊ ڪلاس. حصو III - رابطي
بهتر گڏجي: جاوا ۽ ٿريڊ ڪلاس. حصو IV — سڏڻ لائق، مستقبل، ۽ دوست
گڏ ڪري بهتر: جاوا ۽ ٿريڊ ڪلاس. حصو VI - فائر پري!
GO TO FULL VERSION