مشخصات مقاله
آموزش Java-آموزش Multithreading در جاوا
جاوا یک زبان برنامه نویسی amultithreaded می باشد که به این معناست که می توانیم با استفاده از جاوا، برنامه های چند رشته ای را گسترش دهیم. یک برنامه ی چند رشته ای حاوی دو یا چند بخش است که می توانند به طور همزمان اجرا شوند و هر بخش می تواند یک عملکرد متفاوت را در همان زمان کنترل کند، که از منابع در دسترس بهترین استفاده را می کند، به ویژه وقتی کامپیوتر شما دارای چند CPU باشد.
با تعریف، چند عملکرد زمانی است که چند فرایند دارای منابع پردازش مشترک مانند یک CPU، هستند. Multithreading نظریه ی multitasking را روی برنامه ها گسترش می دهد که می توانید عملکردهای خاص را با یک برنامه ی مجزا در رشته های مجزا تقسیم کنید. هرکدام از این رشته ها می توانند به طور موازی اجرا شوند. OS نه تنها زمان پردازش را بین برنامه های مختلف بلکه بین هر رشته در یک برنامه، تقسیم می کند.
Multithreading شما را قادر می سازد تا برنامه را به روشی بنویسید که چند فعالیت می توانند به طور همزمان در یک برنامه اجرا شوند.
طول عمر یک رشته (Thread):
یک رشته در طول عمر خود دارای مراحل مختلفی است. برای مثال یک رشته متولد می شود، آغاز می شود، اجرا می شود و سپس به اتمام می رسد. نمودار زیر طول عمر کامل یک رشته را نشان می دهد.
مراحل ذکر شده ی بالا در اینجا توضیح داده می شوند:
· New: یک رشته ی جدید طول دوره ی خود را در یک وضعیت جدید آغاز می کند. این رشته در این وضعیت باقی می ماند تا اینکه برنامه رشته را آغاز کند. این رشته نیز با عنوان یک رشته ی متولد شده مورد اشاره قرار می گیرد.
· Runnable: پس از اینکه یک رشته ی تازه متولد شده آغاز به کار می کند، این رشته قابل اجرا می شود. یک رشته در این وضعیت به اجرای عملکرد خود می پردازد.
· Waiting: گاهی اوقات هنگامی که یک رشته منتظر یک رشته ی دیگر برای ادامه ی اجرا می باشد، آن رشته به وضعیت waiting تغییر می یابد. تنها زمانی به وضعیت قابل اجرا بازمی گردد که رشته ی دیگر به رشته ی در حال انتظار پیغام ادامه ی اجرا را می فرستد.
· Timed waiting: یک رشته ی قابل اجرا می تواند وضعیت timed waiting را برای یک زمان میانی مشخص شده وارد کند. یک رشته در این وضعیت ،وقتی زمان میانی به پایان می رسد یا وقتی که رویدادی که منتظر آن است اتفاق می افتد، به حالت قابل اجرا (runnable) با زمی گردد.
· Terminated: یک رشته ی قابل اجرا وقتی عملکرد خود را کامل می کند، وارد وضعیت terminated می شود.
پراپرتی های Thread:
هر رشته ی جاوا دارای یک اولویت می باشد که به سیستم در حال اجرا کمک می کند تا مشخص کند ترتیب در کدام رشته ها برنامه ریزی شده اند.
اولویت های رشته ی جاوا در محدوده ای بین MIN_PRIORITY و MAX_PRIORITY می باشند. به طور پیش فرض به هر رشته اولویت NORM_PRIORITY داده می شود.
رشته ها با اولویت های بالاتر مهم تر از یک برنامه بوده و باید دارای زمان پردازشی قبل از رشته هایی با اولویت پایین تر باشند. به هرحال اولویت های رشته ها نمی تواند تضمین کند که ترتیب در کدام رشته ها و کدام سکوهای وابسته اجرا می شود.
ایجاد یک Thread با اینترفیس implementingRunnable :
اگر گروه شما قرار است به عنوان یک رشته اجرا شود، این امر با استفاده از اینترفیس implementingRunnable قابل اجرا می باشد. لازم است که شما سه مرحله ی اصلی را دنبال کنید:
مرحله ی اول:
به عنوان اولین مرحله نیاز دارید که یک متود run() اجرا کنید که توسط اینترفیس Runnable ارائه شده است. این متود نقطه ی ورودی برای رشته ارائه می دهد و شما کار کامل خود را داخل این روش قرار می دهید. در زیر ترکیب ساده ی متود run() را مشاهده می کنید:
public void run( )
مرحله ی دوم:
ر مرحله ی دوم با استفاده از آبجکت Thread ، سازنده ی زیر را نمونه گذاری می کنید:
Thread(Runnable threadObj, String threadName);
در حالیکه threadObj نمونه ای از یک گروه است که اینترفیس Runnable را اجرا می کند، threadName نام داده شده به رشته می باشد.
مرحله ی سوم:
زمانیکه آبجکت Thread ایجاد می شود، می توانید آن را با فراخوانی متود start( ) آغاز کنید، که یک فراخوانی به متود run( ) اجرا می کند.
در زیر ترکیب ساده ی متود start() را مشاهده می کنید:
void start( );
مثال:
در اینجا مثالی را می بینید که یک رشته ی جدید ایجاد کرده و اجرای آن را آغاز می کند:
class RunnableDemo implements Runnable {
private Thread t;
private String threadName;
RunnableDemo( String name){
threadName = name;
System.out.println("Creating " + threadName );
}
public void run() {
System.out.println("Running " + threadName );
try {
for(int i = 4; i > 0; i--) {
System.out.println("Thread: " + threadName + ", " + i);
// Let the thread sleep for a while.
Thread.sleep(50);
}
} catch (InterruptedException e) {
System.out.println("Thread " + threadName + " interrupted.");
}
System.out.println("Thread " + threadName + " exiting.");
}
public void start ()
{
System.out.println("Starting " + threadName );
if (t == null)
{
t = new Thread (this, threadName);
t.start ();
}
}
}
public class TestThread {
public static void main(String args[]) {
RunnableDemo R1 = new RunnableDemo( "Thread-1");
R1.start();
RunnableDemo R2 = new RunnableDemo( "Thread-2");
R2.start();
}
}
این مثال نتیجه ی زیر را به دنبال دارد:
Creating Thread-1
Starting Thread-1
Creating Thread-2
Starting Thread-2
Running Thread-1
Thread: Thread-1, 4
Running Thread-2
Thread: Thread-2, 4
Thread: Thread-1, 3
Thread: Thread-2, 3
Thread: Thread-1, 2
Thread: Thread-2, 2
Thread: Thread-1, 1
Thread: Thread-2, 1
Thread Thread-1 exiting.
Thread Thread-2 exiting.
ایجاد رشته به وسیله ی گسترش گروه Thread:
دومین راه برای ایجاد یک رشته، ایجاد یک گروه جدید می باشد که با استفاده از دو مرحله ی ساده ی زیر گروه Thread را گسترش می دهد. این دیدگاه با استفاده از متودهایی در گروه Thread، انعطاف پذیری بیشتری در کنترل چند رشته ی ایجاد شده ارائه می دهد.
مرحله ی اول:
لازم است که متود run( ) ، موجود در گروه Thread را نادیده بگیرید. این متود نقطه ی ورودی برای رشته ارائه می دهد و شما کار خود را داخل این متود قرار می دهید. در زیر ترکیب ساده ی مربوط به متود run() را مشاهده می کنید.
public void run( )
مرحله ی دوم:
زمانیکه آبجکت Thread ایجاد شد، شما می توانید آن را با فراخوانی متود run( ) اجرا کنید.
در زیر ترکیب ساده ی مربوط به متود start() را مشاهده می کنید:
void start( );
مثال:
در اینجا برنامه ی قبل برای باز کردن Thread، بازنویسی شده است:
class ThreadDemo extends Thread {
private Thread t;
private String threadName;
ThreadDemo( String name){
threadName = name;
System.out.println("Creating " + threadName );
}
public void run() {
System.out.println("Running " + threadName );
try {
for(int i = 4; i > 0; i--) {
System.out.println("Thread: " + threadName + ", " + i);
// Let the thread sleep for a while.
Thread.sleep(50);
}
} catch (InterruptedException e) {
System.out.println("Thread " + threadName + " interrupted.");
}
System.out.println("Thread " + threadName + " exiting.");
}
public void start ()
{
System.out.println("Starting " + threadName );
if (t == null)
{
t = new Thread (this, threadName);
t.start ();
}
}
}
public class TestThread {
public static void main(String args[]) {
ThreadDemo T1 = new ThreadDemo( "Thread-1");
T1.start();
ThreadDemo T2 = new ThreadDemo( "Thread-2");
T2.start();
}
class ThreadDemo extends Thread {
private Thread t;
private String threadName;
ThreadDemo( String name){
threadName = name;
System.out.println("Creating " + threadName );
}
public void run() {
System.out.println("Running " + threadName );
try {
for(int i = 4; i > 0; i--) {
System.out.println("Thread: " + threadName + ", " + i);
// Let the thread sleep for a while.
Thread.sleep(50);
}
} catch (InterruptedException e) {
System.out.println("Thread " + threadName + " interrupted.");
}
System.out.println("Thread " + threadName + " exiting.");
}
public void start ()
{
System.out.println("Starting " + threadName );
if (t == null)
{
t = new Thread (this, threadName);
t.start ();
}
}
}
public class TestThread {
public static void main(String args[]) {
ThreadDemo T1 = new ThreadDemo( "Thread-1");
T1.start();
ThreadDemo T2 = new ThreadDemo( "Thread-2");
T2.start();
}
}
این مثال نتیجه ی زیر را به دنبال دارد:
Creating Thread-1
Starting Thread-1
Creating Thread-2
Starting Thread-2
Running Thread-1
Thread: Thread-1, 4
Running Thread-2
Thread: Thread-2, 4
Thread: Thread-1, 3
Thread: Thread-2, 3
Thread: Thread-1, 2
Thread: Thread-2, 2
Thread: Thread-1, 1
Thread: Thread-2, 1
Thread Thread-1 exiting.
Thread Thread-2 exiting.
متودهای Thread:
در اینجا لیستی از متودهای مهم موجود در گروه Thread را مشاهده می کنید:
SN |
Methods with Description |
1 |
public void start() رشته را در یک مسیر مجزا از اجرا آغاز می کند، سپس متود run() را در این آبجکت Thread فراخوانی می کند. |
2 |
public void run() اگر آبجکت Thread با استفاده از هدف مجزای Runnable نمونه گذاری شود، متود run() روی آن آبجکت Runnable فراخوانده می شود. |
3 |
public final void setName(String name) نام آبجکت Thread را تغییر می دهد. برای بازیابی نام متود getName() نیز وجود دارد. |
4 |
public final void setPriority(int priority) اولویت آبجکت Thread را تنظیم می کند. مقادیر ممکن بین 1 و 10 می باشند. |
5 |
public final void setDaemon(boolean on) یک پارامتر از true به این رشته به عنوان یک رشته ی daemon مورد اشاره قرار می دهد. |
6 |
public final void join(long millisec) رشته ی جاری این متود را روی دومین رشته درخواست می کند، باعث می شود تا رشته ی موجود تا زمانی که رشته ی دوم تمام می شود یا چند هزارم ثانیه ی مشخص شده به اتمام برسد، مسدود شود. |
7 |
public void interrupt() در این رشته مداخله می کند و باعث می شود تا اگر به هر دلیلی مسدود شده بود، به اجرا ادامه دهد. |
8 |
public final boolean isAlive() اگر رشته زنده باشد، true را گزارش می دهد که هر زمانی پس از شروع رشته می باشد، اما قبل از اینکه برای تکمیل شدن اجرا شود. |
متودهای قبل روی یک آبجکت Thread خاص درخواست می شوند. متودهای زیر در گروه Thread استاتیک می باشند. درخواست یکی از این متودهای استاتیک عملکرد را روی رشته ی فعلی در حال اجرا، اجرا می کند.
SN |
Methods with Description |
1 |
public static void yield() باعث می شود که رشته ی فعلی در حال اجرا تسلیم رشته های دیگریشود که با همان اولویت در انتظار برنامه ریزی شدن هستند. |
2 |
public static void sleep(long millisec) باعث می شود برنامه ی فعلی در حال اجرا حداقل برای چند هزارم ثانیه مسدود شود. |
3 |
public static boolean holdsLock(Object x) اگر رشته ی موجود قفل را روی آبجکت ارائه شده حفظ کند، true را گزارش می دهد. |
4 |
public static Thread currentThread() یک ارجاع به رشته ی در حال اجرای فعلی می دهد که رشته ی در حال اجرا، رشته ای است که این متود را فراخوانده است. |
5 |
public static void dumpStack() stack trace را برای رشته ی فعلی در حال اجرا چاپ می کند که در هنگام عیب یابی یک برنامه ی چند رشته ای مفید می باشد. |
مثال:
برنامه ی ThreadClassDemo زیر برخی از متودهای گروه Thread را توضیح می دهد. یک گروه DisplayMessage را در نظر بگیرید که Runnable را اجرا می کند:
// File Name : DisplayMessage.java
// Create a thread to implement Runnable
public class DisplayMessage implements Runnable
{
private String message;
public DisplayMessage(String message)
{
this.message = message;
}
public void run()
{
while(true)
{
System.out.println(message);
}
}
}
در زیر گروه دیگری را می بینید که گروه Thread را باز می کند:
// File Name : GuessANumber.java
// Create a thread to extentd Thread
public class GuessANumber extends Thread
{
private int number;
public GuessANumber(int number)
{
this.number = number;
}
public void run()
{
int counter = 0;
int guess = 0;
do
{
guess = (int) (Math.random() * 100 + 1);
System.out.println(this.getName()
+ " guesses " + guess);
counter++;
}while(guess != number);
System.out.println("** Correct! " + this.getName()
+ " in " + counter + " guesses.**");
}
}
در زیر برنامه ی مهمی را می بینید که از گروه های تعریف شده ی بالا استفاده می کند:
// File Name : ThreadClassDemo.java
public class ThreadClassDemo
{
public static void main(String[] args)
{
Runnable hello = new DisplayMessage("Hello");
Thread thread1 = new Thread(hello);
thread1.setDaemon(true);
thread1.setName("hello");
System.out.println("Starting hello thread...");
thread1.start();
Runnable bye = new DisplayMessage("Goodbye");
Thread thread2 = new Thread(bye);
thread2.setPriority(Thread.MIN_PRIORITY);
thread2.setDaemon(true);
System.out.println("Starting goodbye thread...");
thread2.start();
System.out.println("Starting thread3...");
Thread thread3 = new GuessANumber(27);
thread3.start();
try
{
thread3.join();
}catch(InterruptedException e)
{
System.out.println("Thread interrupted.");
}
System.out.println("Starting thread4...");
Thread thread4 = new GuessANumber(75);
thread4.start();
System.out.println("main() is ending...");
}
}
این کد نتیجه ی زیر را به دنبال دارد. شما می توانید این مثال را دفعات مختلف امتحان کنید که هر زمان یک نتیجه ی متفاوت دریافت خواهید کرد:
Starting hello thread...
Starting goodbye thread...
Hello
Hello
Hello
Hello
Hello
Hello
Goodbye
Goodbye
Goodbye
Goodbye
Goodbye
.......