با توجه به رشد روزافزون تقاضا برای نوشتن کدهای غیر مسدودکننده، باید روش‌هایی برای اجرای ناهمگام کد پیدا کنیم. در این راهنما چندین روش برای دستیابی به برنامه نویسی ناهمگام در جاوا را مورد بررسی قرار می‌دهیم. همچنین چند کتابخانه جاوا که راه‌حل‌های آماده‌ای به این منظور ارائه می‌کنند را بررسی خواهیم کرد.

برنامه نویسی ناهمگام در جاوا

در این بخش روش‌های برنامه‌نویسی ناهمگام را در جاوا معرفی می‌کنیم.

Thread

امکان ایجاد یک نخ جدید در جاوا برای اجرای ناهمگام یک عملیات وجود دارد. با معرفی عبارت‌های لامبدا در جاوا 8 این کار اینک به روشی تمیزتر و با خوانایی بیشتر نیز انجام می‌یابد.

در ادامه یک نخ جدید می‌سازیم که فاکتوریل یک عدد را محاسبه کرده و پرینت می‌کند:

FutureTask

از جاوا 5 به بعد اینترفیس Future روشی برای اجرای عملیات ناهمگام با استفاده از FutureTask ارائه کرده است. به این ترتیب می‌توانیم از متد submit در xecutorService برای اجرای ناهمگام یک کار بهره بگیریم و وهله‌ای از FutureTask را بازگشت دهیم. از این روش برای یافتن فاکتوریل یک عدد استفاده می‌کنیم:

در این کد از متد isDone که از سوی اینترفیس Future ارائه شده، استفاده می‌کنیم تا ببینیم آیا کار مورد نظر پایان یافته است یا نه. زمانی که کار پایان یافت، می‌توانیم نتیجه را با استفاده از متد get بازیابی کنیم.

CompletableFuture

جاوا 8 امکانی به نام CompletableFuture معرفی کرده است که با Future و CompletionStage ترکیب می‌شود. این قابلیت متدهایی مانند supplyAsync ،runAsync و thenApplyAsync برای برنامه‌نویسی ناهمگام ارائه می‌کند.

در کد زیر از CompletableFuture به جای FutureTask برای یافتن فاکتوریل یک عدد کمک می‌گیریم:

در این جا لزومی به استفاده صریح از ExecutorService وجود ندارد. CompletableFuture به صورت داخلی از ForkJoinPool برای مدیریت ناهمگام کارها بهره می‌گیرد. از این رو کد به مقدار زیادی تمیزتر می‌شود.

Guava

Guava کلاسی به نام ListenableFuture برای اجرای عملیات ناهمگام ارائه کرده است. ابتدا آخرین وابستگی Maven مربوط به Guava را اضافه می‌کنیم:

سپس فاکتوریل عدد را با استفاده از ListenableFuture می‌یابیم:

در این کد کلاس MoreExecutors وهله‌ای از کلاس ListeningExecutorService ارائه می‌کند. سپس متد ListeningExecutorService.submit وظیفه مورد نظر را به صورت ناهمگام اجرا می‌کند و وهله‌ای از ListenableFuture بازگشت می‌دهد. Guava یک کلاس به نام Futures نیز دارد که متدهایی مانند submitAsync ،scheduleAsync و transformAsync برای اتصال به ListenableFutures مانند CompletableFuture ارائه می‌کند.

برای نمونه در کد زیر شیوه استفاده از Futures.submitAsync را به جای متد ListeningExecutorService.submit می‌بینیم:

در کد فوق متد submitAsync نیازمند یک آرگومان AsyncCallable است که با استفاده از کلاس Callables ساخته شده است. به علاوه کلاس Futures متد addCallback را برای ثبت callback-های موفقیت و شکست ارائه می‌کند:

EA Async

Electronic Arts قابلیت async-await را از طریق کتابخانه ea-async از ‎.Net به اکوسیستم جاوا آورده است. این کتابخانه امکان نوشتن کد ناهمگام (غیر مسدودکننده) را به صورت ترتیبی فراهم ساخته است. از این رو برنامه‌نویسی ناهمگام آسان‌تر شده و به صورت طبیعی مقیاس‌پذیر می‌شود. ابتدا آخرین وابستگی Maven مربوط به ea-async را به فایل pom.xml اضافه کنید:

سپس کد CompletableFuture را که قبلاً مورد بررسی قرار دادیم با استفاده از متد await که از سوی کلاس Async ارائه شده است بازنویسی می‌کنیم:

در این کد یک فراخوانی به متد Async.init در بلوک static داریم تا تجهیزِ زمانِ اجرای Async را مقداردهی کنیم. تجهیز Async موجب می‌شود که در زمان اجرا تبدیل شود و فراخوانی به متد await بازنویسی می‌شود تا رفتاری مشابه با استفاده از زنجیره‌ای CompletableFuture داشته باشد.

از این رو فراخوانی به متد await مشابه فراخوانی Future.join است. می‌توانیم از پارامتر  javaagent– در JVM برای تجهیز زمان کامپایل استفاده کنیم. این روش جایگزینی برای متد Async.init محسوب می‌شود:

در ادامه مثال دیگری از نوشتن کد ناهمگام به روش ترتیبی را بررسی می‌کنیم. ابتدا چند عملیات زنجیره‌ای را به صورت ناهمگام با استفاده از متدهای ترکیبی مانند thenComposeAsync و thenAcceptAsync از کلاس CompletableFuture اجرا می‌کنیم:

سپس می‌توانیم کد را با استفاده از ()Async.await مربوط به EA تبدیل کنیم:

این پیاده‌سازی مشابه کد مسدودسازی ترتیبی است. اما متد await موجب می‌شود که کد مسدود نشود. چنانکه اشاره کردیم همه فراخوانی‌ها به متد await از سوی تجهیز Async بازنویسی می‌شوند تا مشابه متد کار کنند.

بنابراین زمانی که اجرای ناهمگام متد hello پایان یابد، نتیجه Future به متد mergeWorld ارسال می‌شود. سپس نتیجه با استفاده از متد CompletableFuture.runAsync به آخرین اجرا فرستاده می‌شود.

Cactoos

Cactoos یک کتابخانه جاوا است بر مبنای مفاهیم شیءگرایی است. کتابخانه کاکتوس جایگزینی برای کتابخانه Guava گوگل و Commons آپاچی محسوب می‌شود که اشیای مشترکی برای اجرای عملیات مختلف ارائه می‌کند. ابتدا آخرین وابستگی Maven مربوط به cactoos را اضافه می‌کنیم:

این کتابخانه یک کلاس Async برای عملیات ناهمگام ارائه می‌کند. بنابراین می‌توانیم فاکتوریل یک عدد را با استفاده از وهله‌ای از کلاس Async کاکتوس پیدا کنیم:

در این کد، متد apply عملیات را با استفاده از متد ExecutorService.submit اجرا می‌کند و یک وهله از اینترفیس Future بازگشت می‌دهد. به طور مشابه کلاس Async متد exec را دارد که همین قابلیت را بدون مقدار بازگشتی ارائه می‌کند.

نکته: کتابخانه کاکتوس در مراحل اولیه توسعه قرار دارد و ممکن است هنوز برای استفاده در محیط پروداکشن مناسب نباشد.

Jcabi-Aspects

Jcabi-Aspects یک حاشیه‌نویسی برای برنامه‌نویسی ناهمگام از طریق جنبه‌های «برنامه‌نویسی جنبه‌گرا» (AOP) در AspectJ ارائه می‌کند. ابتدا آخرین وابستگی Maven مربوط به jcabi-aspects را اضافه می‌کنیم:

کتابخانه jcabi-aspects نیازمند پشتیبانی زمان اجرای AspectJ است. بنابراین وابستگی Maven مربوط به aspectjrt را اضافه خواهیم کرد:

سپس پلاگین jcabi-maven-plugin را اضافه می‌کنیم که جنبه‌های AspectJ را به فایل‌های باینری می‌افزاید. این پلاگین هدف ajc را ارائه می‌کند که همه کارهای مورد نظر ما را انجام می‌دهد:

بنابراین باید از جنبه‌های AOP برای برنامه‌نویسی ناهمگام بهره بگیریم:

زمانی که کد کامپایل می‌شود، این کتابخانه روش AOP را به جای حاشیه‌نویسی Async@ از طریق به‌کارگیری AspectJ تزریق می‌کند. به این ترتیب متد factorialUsingAspect به صورت ناهمگام اجرا می‌شود. بنابراین ابتدا کلاس را با استفاده از دستور Maven کامپایل می‌کنیم:

خروجی پلاگین jcabi-maven- jcabi-maven-plugin به صورت زیر است:

می‌توانیم با نگاه کردن به لاگ‌ها در فایل jcabi-ajc.log بررسی کنیم که آیا کلاس از سوی پلاگین Maben به درستی اجرا شده است یا نه:

سپس کلاس را به صورت یک اپلیکیشن ساده جاوا اجرا می‌کنیم. خروجی به صورت زیر خواهد بود:

بنابراین یک نخ daemon جدید به نام jcabi-async می‌بینیم که از سوی کتابخانه ایجاد شده و به صورت ناهمگام وظیفه را اجرا می‌کند.

سخن پایانی

در این مقاله چندین روش در مورد برنامه‌نویسی ناهمگام در جاوا را بررسی کردیم. در آغاز به بررسی ویژگی‌های داخلی جاوا مانند FutureTask و CompletableFuture برای برنامه‌نویسی ناهمگام پرداختیم. سپس چند کتابخانه مانند EA Async و Cactoos را بررسی کردیم که راه‌حل‌های آماده‌ای عرضه کرده‌اند.

همچنین به بررسی پشتیبانی از اجرای ناهمگام کارها با استفاده از کلاس‌های ListenableFuture و Futures کتابخانه Guava پرداختیم. در نهایت کتابخانه jcabi-AspectJ را بررسی کردیم که قابلیت‌های برنامه‌نویسی جنبه‌گرا را از طریق حاشیه‌نویسی Async@ برای اجرای متدهای ناهمگام ارائه می‌کند. همه کدهای مورد بررسی در این مقاله را می‌توانید در این ریپوی گیت‌هاب (+) ببینید.

اگر این مطلب برای شما مفید بوده است، آموزش‌های زیر نیز به شما پیشنهاد می‌شوند:

==

میثم لطفی (+)

«میثم لطفی» دانش‌آموخته ریاضیات و شیفته فناوری به خصوص در حوزه رایانه است. وی در حال حاضر علاوه بر پیگیری علاقه‌مندی‌هایش در رشته‌های برنامه‌نویسی، کپی‌رایتینگ و محتوای چندرسانه‌ای، در زمینه نگارش مقالاتی با محوریت نرم‌افزار نیز با مجله فرادرس همکاری دارد.

بر اساس رای 1 نفر

آیا این مطلب برای شما مفید بود؟

نظر شما چیست؟

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *