آشنایی با متد Thread.join در جاوا — به زبان ساده
در این راهنما به بررسی متدهای مختلف ()join در کلاس Thread میپردازیم. به این ترتیب بهتفصیل این متدها را بررسی کرده و برخی نمونه کدها ارائه خواهیم کرد. با ما همراه باشید تا با متد Thread.join در جاوا آشنا شوید.
متد ()join نیز همانند متدهای ()wait و ()notify یک مکانیسم همگامسازی بین نخی محسوب میشود.
متد ()Thread.join در جاوا
متد join در کلاس Thread تعریف شده است. متد ()public final void join یک استثنا به شکل InterruptedException ایجاد کرده و منتظر میماند تا این نخ بمیرد. زمانی که متد ()join را روی یک نخ اجرا میکنیم، نخ فراخوانی شده وارد حالت انتظار میشود و در این حالت باقی میماند تا این که ارجاع به نخ خاتمه یابد.
این رفتار را میتوانید در کد زیر ببینید:
1class SampleThread extends Thread {
2 public int processingCount = 0;
3
4 SampleThread(int processingCount) {
5 this.processingCount = processingCount;
6 LOGGER.info("Thread Created");
7 }
8
9 @Override
10 public void run() {
11 LOGGER.info("Thread " + this.getName() + " started");
12 while (processingCount > 0) {
13 try {
14 Thread.sleep(1000);
15 } catch (InterruptedException e) {
16 LOGGER.info("Thread " + this.getName() + " interrupted");
17 }
18 processingCount--;
19 }
20 LOGGER.info("Thread " + this.getName() + " exiting");
21 }
22}
23
24@Test
25public void givenStartedThread_whenJoinCalled_waitsTillCompletion()
26 throws InterruptedException {
27 Thread t2 = new SampleThread(1);
28 t2.start();
29 LOGGER.info("Invoking join");
30 t2.()join;
31 LOGGER.info("Returned from join");
32 assertFalse(t2.isAlive());
33}
با اجرای کد فوق میتوان انتظار دریافت نتایجی مشابه زیر داشت:
1INFO: Thread Created
2INFO: Invoking join
3INFO: Thread Thread-1 started
4INFO: Thread Thread-1 exiting
5INFO: Returned from join
متد ()join میتواند در صورت متوقف شدن نخ ارجاع شده نیز بازگشت یابد. در این حالت، متد یک استثنا به شکل InterruptedException ایجاد میکند.
در نهایت اگر نخ ارجاع یافته، از قبل خاتمه یافته باشد یا آغاز نشده باشد، فراخوانی متد ()join بیدرنگ بازگشت مییابد.
1Thread t1 = new SampleThread(0);
2t1.()join; //returns immediately
متدهای ()Thread.join با Timeout
در صورتی که نخ ارجاع یافته مسدود شود یا پردازش آن مدت زیادی طول بکشد، متد ()join به انتظار خود ادامه میدهد. این حالت در مواردی که نخ فراخوانی شده پاسخگو نباشد، ممکن است موجب بروز مشکل شود. برای مدیریت چنین موقعیتهایی از نسخههای overload شده متد ()join استفاده میکنیم که امکان تعیین یک دوره زمانی خروجی (timeout) را فراهم میسازند.
دو نسخه زماندار وجود دارند که متد ()join را overload میکنند.
- متد public final void join(long millis) استثنای InterruptedException را ایجاد کرده و به مدت نهایتاً millis میلیثانیه صبر میکند تا نخ بمیرد. Timout 0 به معنی انتظار برای همیشه است.
- متد public final void join(long millis,int nanos) یک استثنای InterruptedException ایجاد کرده و نهایتاً تا millies میلیثانیه به علاوه nanos نانوثانیه برای مردن این نخ صبر میکند.
از متد ()join زماندار به روش زیر میتوان استفاده کرد:
1@Test
2public void givenStartedThread_whenTimedJoinCalled_waitsUntilTimedout()
3 throws InterruptedException {
4 Thread t3 = new SampleThread(10);
5 t3.start();
6 t3.join(1000);
7 assertTrue(t3.isAlive());
8}
در این حالت نخ فراخوانی شده برای تقریباً 1 ثانیه صبر میکند تا t3 خاتمه یابد. اگر t3 در این دوره زمانی تمام نشود، متد ()join کنترل را به متد فراخوانی کننده بازمیگرداند.
متد ()join زماندار برای زمانبندی به سیستم عامل تکیه دارد. بنابراین میتوانیم مطمئن باشیم که ()join دقیقاً به مقدار تعیین شده منتظر میماند.
متدهای ()Thread.join و همگامسازی
علاوه بر انتظار تا خاتمه، فراخوانی متد ()join یک تأثیر همگامسازی نیز دارد. ()join یک رابطه «قبلاً رخ داده» (happens-before) ایجاد میکند. همه اقدامات در یک نخ پیش از هر گونه بازگشت موفق از یک ()join روی آن نخ «قبلاً رخ داده» (happens-before) است.
این بدان معنی است که وقتی یک t1 اقدام به فراخوانی ()t2.join میکند، همه تغییراتی که پس از آن روی t2 اعمال شود در بازگشت برای t1 قابل نمایش خواهد بود. با این حال اگر ()join را فراخوانی نکنیم یا از مکانیسمهای دیگر همگامسازی استفاده کنیم، هیچ تضمینی نیست که تغییرات در نخ دیگر، در صورت تکمیل شدن آن نخ، برای نخ کنونی قابل رؤیت باشند.
از این رو گرچه متد فراخوانی ()join به یک نخ در حالت خاتمه یابد بیدرنگ بازگشت مییابد، اما همچنان باید آن را در پارهای موقعیتها فراخوانی کنیم.
در ادامه مثالی از یک کد که به صورت صحیحی همگامسازی شده را میبینید:
1SampleThread t4 = new SampleThread(10);
2t4.start();
3// not guaranteed to stop even if t4 finishes.
4do {
5
6} while (t4.processingCount > 0);
برای همگامسازی صحیح کد فوق میتوانیم ()t4.join زماندار را درون یک حلقه اضافه کنیم یا از مکانیسمهای دیگر همگامسازی بهره بگیریم.
سخن پایانی
متد ()join برای همگامسازی درون نخی کاملاً مفید است. در این مقاله به بررسی متدهای ()join و رفتار آنها پرداختیم. همچنین کدهایی که از متد ()join استفاده میکنند را بررسی کردیم. کد کامل موارد مطرح شده در این مقاله را میتوانید در این ریپوی گیتهاب (+) ملاحظه کنید.
اگر این مطلب برای شما مفید بوده است، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- مجموعه آموزشهای جاوا (Java)
- مجموعه آموزشهای برنامهنویسی
- گنجینه آموزشهای جاوا (Java)
- زبان برنامه نویسی جاوا (Java) — از صفر تا صد
- برنامهنویسی ناهمگام در جاوا — به زبان ساده
- Thread چیست ؟ — به زبان ساده و جامع
==