CodeGym /جاوا بلاگ /Random-SD /گڏو گڏ بهتر: جاوا ۽ ٿريڊ ڪلاس. حصو V - ايگزيڪيوٽر، ٿريڊ پ...
John Squirrels
سطح
San Francisco

گڏو گڏ بهتر: جاوا ۽ ٿريڊ ڪلاس. حصو V - ايگزيڪيوٽر، ٿريڊ پول، فورڪ/شامل

گروپ ۾ شايع ٿيل

تعارف

تنهن ڪري، اسان ڄاڻون ٿا ته جاوا موضوع آهن. توھان ان جي باري ۾ پڙھي سگھوٿا جائزي ۾ عنوان سان گڏ بهتر: جاوا ۽ ٿريڊ ڪلاس. حصو I - عملدرآمد جا سلسلا . گڏو گڏ بهتر: جاوا ۽ ٿريڊ ڪلاس.  حصو V - ايگزيڪيوٽر، ٿريڊ پول، فورڪ/جوائن - 1اچو ته عام ڪوڊ تي هڪ ٻيو نظر وٺو:
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ٿريڊ تي شروع ڪرڻ لاءِ ڪوڊ لکون ٿا. اهو عظيم آهي، اهو ناهي؟ پر هي صرف شروعات آهي: گڏو گڏ بهتر: جاوا ۽ ٿريڊ ڪلاس.  حصو V - ايگزيڪيوٽر، ٿريڊ پول، فورڪ/جوائن - 2

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. صرف ڪم کي متاثر ڪندڙ پيٽرولر تبديل ڪيا ويا آهن. گڏو گڏ بهتر: جاوا ۽ ٿريڊ ڪلاس.  حصو V - ايگزيڪيوٽر، ٿريڊ پول، فورڪ/جوائن - 3

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);
پر هن معاملي ۾، ڪم هر عمل جي وچ ۾ هڪ مخصوص وقفي سان ڪيو ويندو آهي. اهو آهي، task1 سيڪنڊ کان پوء عمل ڪيو ويندو. ان کان پوء، جيترو جلدي مڪمل ڪيو ويندو، 2 سيڪنڊ گذري ويندا، ۽ پوء هڪ نئون ڪم شروع ڪيو ويندو. هتي هن موضوع تي ڪجهه اضافي وسيلا آهن: گڏو گڏ بهتر: جاوا ۽ ٿريڊ ڪلاس.  حصو V - ايگزيڪيوٽر، ٿريڊ پول، فورڪ/جوائن - 4

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 - فائر پري!
تبصرا
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION