کنترل ترتیب اجرای تردها در زبان برنامه نویسی جاوا


در آموزش های گذشته با مفهوم تردها در زبان برنامه نویسی جاوا آشنا شدیم. در این آموزش قصد داریم تا ببینیم که به روشی می توانیم ترتیب اجرای تردهای ایجاد شده را کنترل نماییم. در ابتدا پروژه ای در محیط برنامه نویسی اکلیپس تحت عنوان Controlling Thread Execution ایجاد کرده و کلاسی در آن به نام HowToControlThreads به معنی «چگونه Thread ها را کنترل کنیم» می سازیم:

public class HowToControlThreads extends Thread {
    @Override
    public void run() {
        System.out.println("This is text from first thread 1");
        System.out.println("This is text from first thread 2");
        System.out.println("This is text from first thread 3");
        System.out.println("This is text from first thread 4");
        System.out.println("This is text from first thread 5");
        System.out.println("This is text from first thread 6");
        super.run();
    }
}

همان طور که قبلاً توضیح داده شد، دو روش متفاوت برای ایجاد یک ترد در زبان برنامه نویسی جاوا وجود دارد که یکی ارث بری از کلاس Thread جاوا است و دیگری استفاده از Runnable است. در مثال بالا از روش اول استفاده کرده ایم به این صورت که با نوشتن کلید واژه extends سپس نوشتن نام کلاس Thread، کلاس خود را مجبور به ارث بری از کلاس Thread جاوا کرده ایم. قبلاً توضیح دادیم که هر آنچه بخواهیم در این کلاس اجرا شود می بایست داخل متد run قرار گیرد. خروجی Thread یی که در بالا ایجاد کرده ایم نمایش شش عبارت در پنجره کنسول اکلیپس است.

اکنون برای آن که بتوانیم این ترد را اجرا کنیم، می بایست کلاس دیگری تحت عنوان ActionClass ایجاد کرده و این بار گزینه public static void main را تیک بزنیم چرا که این کلاس به منزله نقطه شروع برنامه جاوای ما خواهد بود:

public class ActionClass {
    public static void main(String[] args) {
        HowToControlThreads myObject1 = new HowToControlThreads();
        myObject1.start();
    }
}

داخل متد main یک آبجکت تحت عنوان object از روی کلاس HowToControlThread ایجاد کرده سپس متد start را به آن ضمیمه می کنیم تا ترد ما اجرا شود. حال می توانیم برنامه خود را اجرا کنیم:

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

public class MySecondThread extends Thread {
    @Override
    public void run() {
        System.out.println("This is text from second thread 1");
        System.out.println("This is text from second thread 2");
        System.out.println("This is text from second thread 3");
        System.out.println("This is text from second thread 4");
        System.out.println("This is text from second thread 5");
        System.out.println("This is text from second thread 6");
        super.run();
    }
}

همان طور که می بینیم، کلاسی تحت عنوان MySecondThread ایجاد کرده و آن را از کلاس Thread جاوا extends کرده ایم. سپس داخل متد run این ترد، شش دستور نوشته ایم تا داخل پنجره ی کنسول به نمایش درآیند (لازم به ذکر است که این بار از واژه second به معنی «دوم» استفاده کرده ایم تا متوجه شویم که از ترد دوم است.) حال می بایست تا آبجکتی از روی این کلاس داخل کلاس ActionClass ایجاد کنیم:

public class ActionClass {
    public static void main(String[] args) {
        HowToControlThreads myObject1 = new HowToControlThreads();
        myObject1.start();
        MySecondThread myObject2 = new MySecondThread();
        myObject2.start();
    }
}

همان طور که می بینیم، آبجکتی تحت عنوان myObject2 از روی کلاس MySecondThread ایجاد کرده سپس برای آن که بتوانیم ترد داخل آن را اجرا کنیم، متد start را به نام آبجکت ساخته شده از روی آن کلاس ضمیمه کرده ایم. در این مرحله از کار، برنامه ی خود را مجدد اجرا می کنیم:

می بینیم که هر دو ترد اجرا شده و اجرای آن ها به این شکل است که هر کدام شانس بیشتری برای اجرا پیدا کنند، اول اجرا خواهد شد. یک بار دیگر برنامه را اجرا می کنیم:

می بینیم که در اجرای دوم، تردها به ترتیب اجرا شده اند و این مسئله کاملاً تصادفی است. نکته جدیدی که در این آموزش می خواهیم مد نظر قرار دهیم این است که ببینیم به چه صورت می توانیم کنترل اجرای تردها را در دست بگیریم. برای این منظور نیاز است تا از اینترفیسی تحت عنوان ExecutorService استفاده کنیم. برای این منظور کد فوق را به صورت زیر تکمیل می کنیم:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ActionClass {
    public static void main(String[] args) {
        HowToControlThreads myObject1 = new HowToControlThreads();
        // myObject1.start();
        MySecondThread myObject2 = new MySecondThread();
        // myObject2.start();
        ExecutorService myController = Executors.newSingleThreadExecutor();
        myController.submit(myObject1);
        myController.submit(myObject2);
    }
}


اولین کاری که انجام می دهیم این است که اجرای تردها را بایستی متوقف کنیم. برای این منظور متدهای start مرتبط با نام آبجکت های ساخته شده از روی هر دو ترد را کامنت می کنیم. سپس همان طور که می بینیم، ابتدا نام اینترفیسی یی تحت عنوان ExecutorService را نوشته و نامی همچون myController برای آن در نظر می گیریم. در ادامه یک علامت مساوی قرار داده و نام کلاسی تحت عنوان Executors را می نویسیم. کاری که این کلاس انجام می دهد این است که حاوی تعدادی متد از پیش تعریف شده است که این امکان را به ما می دهند تا بتوانیم رفتار تردهای خود را کنترل کنیم. در ادامه پس از نوشتن نام کلاس Executors یک نقطه قرار داده و متدی همچون newSingleThreadExecutor را انتخاب می کنیم. همان طور که در تصویر زیر مشخص است، به محض قرار دادن یک نقطه پس از کلاس Executors به کلیه متدهای این کلاس دسترسی خواهیم داشت:

در این مرحله برای آن که بتوانیم تردهای خود را به ترتیب خاصی اجرا کنیم، ابتدا بایستی نام آبجکت ساخته شده از روی اینترفیسی یی تحت عنوان ExecutorService را نوشته سپس متدی تحت عنوان submit را به آن ضمیمه کنیم. سپس پارامتر این متد را بایستی نام آبجکت های ساخته شده از روی تردها قرار دهیم. حال مجدد برنامه خود را اجرا می کنیم:

می بینیم که هر یک از تردها به ترتیبی که در متد submit آمده است اجرا شده اند و دیگر این احتمال وجود ندارد که هر تردی که شانس اول را برای اجرا شدن پیدا کند اول اجرا شود. اگر بخواهیم این مسئله را با حالت پیش مقایسه کنیم می توانیم کد خود را به صورت زیر ویرایش کرده سپس برنامه را مجدد اجرا کنیم:

public class ActionClass {
    public static void main(String[] args) {
        HowToControlThreads myObject1 = new HowToControlThreads();
        myObject1.start();
        MySecondThread myObject2 = new MySecondThread();
        myObject2.start();
        // ExecutorService myController = Executors.newSingleThreadExecutor();
        // myController.submit(myObject1);
        // myController.submit(myObject2);
    }
}

همان طور که می بینیم، ابتدا متدهای start را از حال کامنت در آورده سپس ExecutorService را کامنت می کنیم و مجدد برنامه خود را اجرا می کنیم:

می بینیم که تردها به صورت تصادفی اجرا شده اند (لازم به ذکر است که پس از اجرای برنامه با این کد ممکن است مجدد هر یک از تردها به ترتیب اجرا شوند و نتایجی مشابه نتایج پیش را مشاهده کنیم اما این در حالی است که چنین نتیجه ای کاملاً تصادفی بوده و در صورتی که برنامه خود را چند بار پشت سر هم اجرا کنیم هر دفعه می توانیم انتظار نتیجه متفاوتی را داشته باشیم.) در خاتمه بایستی گفت که متدهای مرتبط با کلاس Executors زیاد هستند و بسته به نیازی که شما در پروژه های خود در آینده خواهید داشت، می توانید از آن ها استفاده نمایید.

دانلود فایل‌های تمرین
لیست نظرات
کاربر میهمان
دیدگاه شما چیست؟
کاربر میهمان
hamid.d.moghad
hamid.d.moghad
همه فصل ها تا اینجا خوب بودن الا این فصل 13 . کلا از این فصل هیچی نفهمیدم. تو این خط کد
ExecutorService myController = Executors.newSingleThreadExecutor();
بعد از نوشتن اکسکیوتر و زدن نقطه هیچ متدی برام نمایش داده نمیشه و خودم هم که متد رو دستی میزنم ارور میگیره. در ضمن کلاسش رو هم ایمپورت کردم و لی در این قسمت به مشکل برخوردم.
کاربر میهمان
کاربر میهمان
آموزشی از پایه غلط. بیچاره کسانی که اطلاعی از صحیح بودن آموزش ندارن و فکر می‌کنن درحال یادگیری چیزی هستند.
muhamad64
muhamad64
سلام
میشه لطفا راجع به این خط توضیح بدین
ExecutorService myController = Executors.newSingleThreadExecutor
اگر آبجتک داریم میسازیم پس چرا new ندارد از اینترفیس هم که نمیتوان آبجکت ساخت ؟؟
muhamad64
muhamad64
کلا توضیحات خیلی خوب است
ولی این مبحث برای من سخت است
متوجه نشدم چرا در متد run مجدد run را فرا میخواند کاری با بحث super ندارم لطفا راهنمایی نمایید
کاربر میهمان
کاربر میهمان
سلام و ممنون از آموزش های خوبتون
ببخشید کاربرد() super.run چیه چون من امتحان کردم توی ترتیب اجرای ترد ها تاثیری نداشت
کاربر میهمان
کاربر میهمان
من وقتی ترد ها رو استارت میکنم ترد ها به ترتیب اجرا میشن و تصادفی اجرا نمیشن.چند بار اجرا کردم ولی به ترتیب اجرا شدن.با استفاده از استارت.
Farshid
Farshid
سلام
با تشکر بابت آموزش خوبتون.
سوالم اینه که میشه با setPriority هم تردها رو نوبت دهی کرد. این روش شما بهتره یا با استفاده از setPriority؟
@HOSEIN@
@HOSEIN@
باسلام وخسته نباشید.
من به یه مشکلی برخوردم
آقابهزادشماتو اول فصل3گفتیدازروی interface هانمیشه object ساخت
ولی تواین بخش گفتید :
بایستی نام شیئ ساخته شده از روی Interface یی تحت عنوان ExecutorService را بنویسیم
من آخرمتوجه نشدم میشه ازروی interfaceها object ساخت یانه,اگه میشه راهناییم کنید
باتشکر
CIna Deuxshiri
CIna Deuxshiri
سلام. نه نمیشه از روی اینترفیس آبجکت ساخت. اما از روی اون کلاسی که اینترفیس و پیاده‌سازی کرده میشه ساخت. در اینجا هم منظور همین بوده.

در پاسخ به

کاربر میهمان
کاربر میهمان
سلام، خسته نباشید.
اینجا هدف از نوشتن super.run(); چی هست ؟ اخه بودو نبودش تاثیری نداره که
مرسی
محمدتقی کاوسی
محمدتقی کاوسی
منم نفهمیدم داستان super.run(); چی هست! یکی توضیح بده خواهشاً

در پاسخ به

Ehsan.Mk
Ehsan.Mk
سلام
شما میتونید یبار با اکلیپس امتحان کنید ، یکبار فقط کلاس اول رو با super.run و یکبار فقط کلاس دوم رو با super.run اجرا کنید . متوجه میشید که کاربرد super در اینجا چیه ... مشاهده میکنید که کلاسی که از super استفاده میکنید اول اجرا نمیشه و بعنوان subclass در نظر گرفته میشه و کلاسی که super توی اون قرار نگرفته اول اجرا میشه و بعنوان superclass در نظر گرفته میشه ... در واقع از این روش میشه برای اولویت بندی اجرای دو ترد (Thread) استفاده کرد . :) :) :)

در پاسخ به

CIna Deuxshiri
CIna Deuxshiri
سلام. این موضوع مربوط میشه به مبحث ارث بری. شما اون مبحث و خوب مطالعه کنید متوجه میشین.

در پاسخ به

کاربر میهمان
کاربر میهمان
سلام
قبل از اعمال ExecutorService هم هر چند بار که برنامه را اجرا می کنم تمام تردها را به ترتیب پرینت می گیرد !! چرا این گونه است؟!!!
Soroush
Soroush
در کلاس ActionClass، قبل از اعمال ExecutorService، خطوط مربوط به start دو آبجکت ساخته شده را درست پشت سر هم بنویسید. در این صورت در اجراهای مختلف ترتیب خروجی ها متفاوت خواهد بود.

در پاسخ به