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


پس از آشنایی با مفاهیم ترد و اینترفیس در دو آموزش گذشته، در این آموزش قصد داریم تا به صورت عملی اقدام به ساخت یک ترد در زبان برنامه نویسی جاوا نماییم. به طور کلی می توان گفت که به دو روش مختلف می توانیم اقدام به ساخت یک ترد نماییم:

- روش اول: به این صورت است که کلاسی ایجاد کرده سپس آن کلاس ویژگی های خود را از کلاس Thread جاوا به ارث ببرد. 
- روش دوم: به این صورت است که کلاسی ایجاد کرده و آن کلاس به اجرای یک اینترفیس تحت عنوان Runnable بپردازد (لازم به ذکر است کلاسی که اقدام به اجرای Runnable نماید بدون آن که خصوصیتی را از کلاس Thread به ارث ببرد، خواهد توانست اقدام به ساخت یک Thread نماید.)

در ادامه آموزش، هر دو روش را مورد بررسی دقیق قرار خواهیم داد. برای شروع یک پروژه جدید تحت عنوان Thread ایجاد کرده و کلاسی در آن تحت عنوان ThreadA ایجاد می کنیم:

public class ThreadA extends Thread {

}

همان طور که در کد فوق مشاهده می شود، کلاسی که تحت عنوان ThreadA ایجاد کردیم کلیه خصوصیات، ویژگی ها و متدهای کلاس Thread جاوا را به ارث خواهد برد. حال کد فوق را به صورت زیر تکمیل می کنیم:

public class ThreadA extends Thread {
    @Override
    public void run() {
        System.out.println("Thread A");
        for (int i = 1; i <= 5; i++) {
            System.out.println("From thread A loop no= " + i);
        }
    }
}

می بینیم که ابتدا متدی تحت عنوان run را وارد برنامه خود کرده ایم. لازم به ذکر است زمانی که یک کلاس از کلاس Thread ارث بری می کند می بایست این متد را Override کرد که در غیر این صورت اکلیپس دوره واژه run را نقطه چین قرار خواهد داد و چنانچه نشانگر موس خود را روی نقطه چین قرار دهیم، اکلیپس به ما پیشنهاد می دهد که این متد را Override کنیم.

حال از آنجا که قصد داریم علاوه بر کلاسی تحت عنوان ThreadA کلاس دیگری تحت عنوان ThreadB نیز ایجاد کنیم، در متد run دستوری را می نویسیم مبنی بر این که عبارت Thread A را در پنجره کنسول به نمایش در آورد تا بدانیم که خروجی برنامه متعلق به کدام Thread است. در ادامه یک لوپ یا حلقه از جنس for تعریف می کنیم. در واقع هر کاری که بخواهیم Thread ما انجام دهد می بایست داخل متد run نوشته شود.

متغیری از جنس int تحت عنوان i ایجاد می کنیم به این شکل که نقطه شروع لوپ عدد 1 خواهد بود، نقطه پایان لوپ کوچکتر یا برابر با 5 و در هر بار لوپ شدن، یک واحد به متغیر i اضافه خواهد شد. سپس داخل for دستور ;()System.out.println را نوشته سپس داخل پرانتز متد println عبارت :From Thread A loop no را می نویسیم که حاکی از آن است که «از Thread A به همراه لوپ شماره:» را در پنجره کنسول به نمایش در آورد. سپس یک علامت + قرار داده و نام متغیری که داخل for ایجاد کردیم را می نویسیم. اکنون کلاس دیگری تحت عنوان ThreadB ایجاد کرده و آن را به شکل زیر تکمیل می کنیم:

public class ThreadB extends Thread {
    @Override
    public void run() {
        System.out.println("Thread B");
        for (int i = 1; i <= 5; i++) {
            System.out.println("From thread B: loop no = " + i);
        }
    }
}

همان طور که ملاحظه می شود این کلاس نیز از کلاس Thread ارث بری می کند و تنها تفاوتی که با کلاس ThreadA دارد این است که هر کجا که حرف A داشتیم به حرف B تغییر یافته است. مجدد اقدام به ایجاد کلاس دیگری تحت عنوان ActionClass می نماییم و به خاطر داشته باشیم که در حین ساخت این کلاس حتماً گزینه public static void main را تیک بزنیم چرا که این کلاس به منزله نقطه شروع برنامه ما خواهد بود:

public class ActionClass {
    public static void main(String args[]) {
    }
}

نیاز است تا از روی هر کدام از کلاس های ThreadA و ThreadB یک آبجکت ایجاد کنیم. برای این منظور کد فوق را به صورت زیر تکمیل می کنیم:

public class ActionClass {
    public static void main(String args[]) {
        ThreadA a = new ThreadA();
        ThreadB b = new ThreadB();
    }
}

همان گونه که در آموزش های پیشین توضیح داده شد، ابتدا نام کلاس مد نظر را نوشته سپس نامی برای آبجکتی که می خواهیم از روی آن کلاس ایجاد کنیم در نظر می گیریم. سپس یک علامت مساوی قرار داده و از آنجا که می خواهیم یک آبجکت جدید ایجاد کنیم می بایست از کلید واژه ی new استفاده نماییم. سپس مجدد نام کلاس مد نظر را نوشته و یک علامت ;() پس از آن قرار می دهیم.

اکنون برای آن که بتوانیم آبجکت های ساخته شده از روی کلاس Thread را اجرا نماییم نیاز به متدی تحت عنوان start داریم. برای این منظور همان طور که در کد زیر مشخص است متد start را به آبجکت های ساخته شده از روی کلاس های ThreadA و ThreadB ضمیمه می کنیم:

public class ActionClass {
    public static void main(String args[]) {
        ThreadA a = new ThreadA();
        a.start();
        ThreadB b = new ThreadB();
        b.start();
    }
}

به طور خلاصه آبجکت ساخته شده از روی کلاس ThreadA نامش a است و آبجکت ساخته شده از روی کلاس ThreadB نامش b است. حال متد start را به هر دوی این آبجکت ها ضمیمه می کنیم. اکنون اگر برنامه خود را اجرا کنیم با خروجی زیر مواجه خواهیم شد:

همان طور که در اجرای فوق می بینیم اول Thread مرتبط با کلاس ThreadB اجرا شده است سپس Thread مرتبط با کلاس ThreadA. برای دومین بار برنامه را اجرا می کنیم:

می بینیم که این بار ترتیب اجرای Thread ها عکس اجرای قبل است. در حقیقت علت این مسئله از آنجا ناشی می شود که در یک برنامه جاوا، تردها نه تنها به صورت هم زمان اجرای می شوند بلکه این در حالی است که هر دو ترد مستقل از یکدیگر اجرا می شوند و به همین دلیل نیز می باشد که هر کدام ابتدا شانس اجرا شدن را پیدا کند، اول اجرا خواهد شد. حال فرض کنیم که می خواهیم یک ترد به روش دومی که در بالا به آن اشاره شد ایجاد کنیم. برای این منظور پروژه دیگری تحت عنوان Runnable ایجاد کرده و کلاسی در آن به نام MyThread ایجاد می کنیم:

public class MyThread {

}

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

public class MyThread implements Runnable {
    @Override
    public void run() {
        for (int i = 1; i <= 5; i++) {
            System.out.println("From My Thread loop no:" + i);
        }
    }
}

ابتدا کلاس MyThread، با استفاده از کلید واژه implements اقدام به اجرای Runnable خواهد کرد. سپس متد مرتبط با ایجاد یک ترد که run نام دارد را وارد برنامه خود می کنیم (به خاطر داشته باشیم برای آن که بتوانیم از این متد استفاده کنیم، می بایست آن را Override کنیم.) سپس همانند تردهای قبلی، یک لوپ از جنس for ایجاد کرده و کدهای قبلی را برای آن در نظر می گیریم. در این مرحله از کار نیاز است تا کلاس دیگری تحت عنوان ActionClass ایجاد کنیم که به منزله نقطه شروع برنامه ما خواهد بود (به خاطر داشته باشیم که گزینه public static void main را تیک دار نماییم):

public class ActionClass {
    public static void main(String[] args) {
    }
}

در ادامه بایستی یک آبجکت از روی کلاسی که تحت عنوان MyThread ایجاد کردیم در این کلاس ایجاد نماییم. برای این منظور کد فوق را به صورت زیر تکمیل می کنیم:

public class ActionClass {
    public static void main(String[] args) {
        MyThread test = new MyThread();
    }
}

نام آبجکتی که از روی کلاس MyThread ایجاد کرده ایم test است. اگر خاطرمان باشد در روش اول ساخت یک ترد، نام آبجکت ساخته شده از روی کلاس خود را مستقیماً به متد start ضمیمه می کردیم و ترد ما اجرا می شد اما این در حالی است که زماین که از Runnable برای ایجاد یک ترد استفاده می کنیم، می بایست آبجکتی از روی کلاس Thread نیز ایجاد کرده، آن آبجکت را به آبجکتی که از روی کلاس خود ساختیم مرتبط ساخته و در نهایت آبجکت ساخته شده از روی کلاس Thread را به متد start ضمیمه کنیم. برای این منظور کد فوق را به صورت زیر تکمیل می کنیم:

public class ActionClass {
    public static void main(String[] args) {
        MyThread test = new MyThread();
        Thread thread = new Thread(test);
        thread.start();
    }
}

همان طور که ملاحظه می شود، یک آبجکت از روی کلاس Thread جاوا تحت عنوان thread ایجاد کرده ایم (اگر توجه کنیم می بینیم که آبجکت ساخته شده از روی کلاس Thread با حرف کوچک نوشته شده است. به جای این نام می توانیم از هر نام دیگری نیز استفاده کنیم.) سپس داخل پرانتز مرتبط با کلاس Thread نام آبجکتی تحت عنوان test که از روی کلاس MyThread ایجاد کردیم را نوشته و در نهایت آبجکت thread را به متد start که وظیفه دارد یک ترد را ایجاد کند ضمیمه می کنیم. اگر برنامه خود را اجرا کنیم با خروجی زیر مواجه خواهیم شد:

شاید مطالب این آموزش تا حدودی سخت به و غیر قابل فهم به نظر برسند، اما این در حالی است که با تکرار و تمرین و استفاده از تردها و اینترفیس ها در پروژه های واقعی، این مفاهیم بیشتر درک خواهند شد.

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

در این آموزش من به یه اختلاف رسیدم که خواستم در میون لگذارم تا ااگر جایی اشتباه میکنم لطف کنید و کمکم کنید اصلاحش کنم:

اول بگم که من همطمان با خواند هر دوره حط به حط برنامه پیشنهادی دوره رو مینویسم و اجرا میکنم. حتی در بعضی موراد کدها رو برای بهتر فهمیدن، جابجا میکنم.
در توضیح ائلیه Thread فرموده بودید که کمک میکنه تا دستورات همزمان اجرا بشن. اما در اجرای بخض اول آموزش (فراخوانی Thread از حود جاوا) وقتی خروجی برنامه رو گذاشتید، اول ThreadA و بعد ThreadB اجرا شدن (و در سری دوم اجرا برعکس) که این خود موضوع همزمانی رو زیر سوال میبره.
خودم که کد رو نوشتم و اجرا کردم خروجی در کنسول همزمانی رو نشون میداد. درواقع یک خط از ThreadA و بعد یک خط از ThreadB اجرا میشد (که برای من معنی همزمانی رو داشت.حالا دو تا سوال وحود داره:
1- با توحه به اینکه کدهای من خط به خط با کدهای شما یکی بود، چرا خروجی برنامه مون متفاوت بود؟
2-چرا خروجی برنامه شما همزمانی اجرای دو tREAD رو نشون نمیده؟

مممنونم بابت وقت و انرژی که میذارید
muhamad64
muhamad64
کلا توضیحات خیلی خوب است
ولی این مبحث برای من سخت است
متوجه نشدم چرا در متد run مجدد run را فرا میخواند کاری با بحث super ندارم لطفا راهنمایی نمایید
shayansiamakan
shayansiamakan
سلام و خسته نباشید خدمت آقای مرادی و تیم سکان آکادمی
یه سوال داشتم وقتی یه threadرو اجرا میکنم بیشتر از سی پی یو استفاده میشه نسبت به زمانی که هردو رو با هم اجرا میکنم چرا؟🙁
ashkan
ashkan
با این‌که ‌همه آموزش هارو‌سریع فهمیدم. این آموزش رو به کلی نفهمیدم. اگر روش اول و دوم و جداگونه از اول با توضیحات پایه ای میومدید جلو بهتر بود. اصلا هیچی‌متوجه نشدم
کاربر میهمان
شهریاریمن یک کاربر مهمان هستم
بسیار عالی و روان و دقیق. خدا خیرت بده داداش.
کاربر میهمان
parisaمن یک کاربر مهمان هستم
آموزشاتون فوق العاده و روانه
ممنون بابت تمامي زحماتتون
کاربر میهمان
سجادمن یک کاربر مهمان هستم
سلام . ممنون بابت اموزشتون . میخواستم بگم تو روش اول شما متدهایی با نام run ایجاد کردید بعد موقع شی سازی یا ابجکت ساختن اسم a و b رو با ()start نوشتید نباید اینجا از ()run استفاده میکردیم .
کاربر میهمان
alirezaمن یک کاربر مهمان هستم
مثا اینکه بعضی از دوستان به اشتباه ایراد گرفتن و متوجه نشدن اون کلاس AوB به روش ارث بری از کلاس ترد و به روش اول ساخت ترد نام گذاری شدن و اصلا به My Thread ربطی ندارن کلاس My Thread به روش دوم یعنی Runnable ساخته شده و اصلا ربطی به اون دوتا کلاس اول نداره
کاربر میهمان
alirezaمن یک کاربر مهمان هستم
ممنون بایت زحماتتون
کاربر میهمان
asdsaمن یک کاربر مهمان هستم
آقا به ما بگو فرق استفاده از اینترفیس Runnable با ارث بری از کلاس ترد چیه