JavaRush /جاوا بلاگ /Random-SD /توھان جاوا کي ھڪڙي موضوع سان خراب نٿا ڪري سگھو: حصو V - ا...
Viacheslav
سطح

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

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

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توهان کي ڪم مڪمل ڪرڻ لاء انتظار ڪرڻ جي اجازت ڏئي ٿو. ان کي استعمال ڪرڻ جا ڪيترائي طريقا آهن: توھان جاوا کي ھڪڙي سلسلي سان برباد نٿا ڪري سگھو: حصو V - ايگزيڪيوٽر، ٿريڊ پول، فورڪ جوائن - 5هي تصوير Alexey Shipilev جي رپورٽ مان هڪ سلائيڊ جو حصو آهي “ فورڪ/جوائن: عملدرآمد، استعمال، ڪارڪردگي .” ان کي واضح ڪرڻ لاءِ، ان جي رپورٽ ڏسڻ جي قابل آهي JEE CONF: “ فورڪ جوائن تي عملدرآمد جون خاصيتون .”

اختصار ڪرڻ

تنهن ڪري، اسان هتي آهيون، جائزو جي ايندڙ حصي کي ختم ڪري رهيا آهيون. Executorاسان اهو معلوم ڪيو ته ڇا اسان پهريون ڀيرو ٿريڊز تي عمل ڪرڻ لاءِ آيا آهيون . پوءِ اسان ان خيال کي جاري رکڻ جو فيصلو ڪيو ۽ ان سان گڏ آياسين ExecutorService. ExecutorServiceتوهان کي اجازت ڏئي ٿو ته ڪمن کي موڪلڻ لاءِ استعمال ڪندي submit۽ invokeانهي سان گڏ ان کي بند ڪري سروس جو انتظام ڪريو. ڇاڪاڻ ته ExecutorService'اسان کي لاڳو ڪرڻ جي ضرورت آهي، اسان فيڪٽري طريقن سان هڪ ڪلاس لکيو ۽ ان کي سڏيو Executors. اهو توهان کي ٿريڊ پول ٺاهڻ جي اجازت ڏئي ٿو ThreadPoolExecutor. ساڳي ئي وقت ۾، اهڙا ٿريڊ پول آهن جيڪي توهان کي اجازت ڏين ٿا ته عمل لاءِ شيڊول بيان ڪن، پر WorkStealingPoolلڪائي ٿو ForkJoinPool. مون کي اميد آهي ته جيڪو مٿي لکيو ويو هو صرف توهان لاء دلچسپ نه هو، پر سمجھڻ وارو) مان هميشه خوش آهيان مشورا ۽ رايا حاصل ڪرڻ لاء. #وياچسلاو
تبصرا
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION