JVM کاری را که میخواهد انجام میدهد، بنابراین چگونه میتوانید ترتیب اجرای thread را پیشبینی کنید؟
- اولین رشته خود را پیدا کنید: روش main() جاوا
- چرخه حیات رشته جاوا
- توسعه یک کلاس رشته
- رابط Runnable
- رشتههای غیر شبح در مقابل دیمون
- اولویت موضوع و JVM
- چه چیزی را در مورد رشته های جاوا به خاطر بسپارید
- اشتباهات رایج در موضوعات جاوا
- چالش موضوعات جاوا را قبول کنید!
- درباره جاوا بیشتر بیاموزید
Threading به تمرین اجرای همزمان فرآیندهای برنامه نویسی برای بهبود عملکرد برنامه اشاره دارد. در حالی که کار با رشته ها به طور مستقیم در برنامه های تجاری معمول نیست، آنها همیشه در چارچوب های جاوا استفاده می شوند. به عنوان مثال، چارچوب هایی که حجم زیادی از اطلاعات را پردازش می کنند، از رشته ها برای مدیریت داده ها استفاده می کنند. دستکاری رشتهها یا فرآیندهای CPU به طور همزمان عملکرد را بهبود میبخشد و در نتیجه برنامههای سریعتر و کارآمدتری ایجاد میکند.
این مقاله شما را با برخی از اصول رشته های سنتی جاوا و اجرای رشته در ماشین مجازی جاوا آشنا می کند. InfoWorld مقدمه Project Loom را ببینید تا در مورد رشته های مجازی و مدل جدید همزمانی ساختاریافته جاوا بیاموزید.
اولین رشته خود را پیدا کنید: روش main() جاوا
حتی اگر هرگز مستقیماً با رشتههای جاوا کار نکردهاید، بهطور غیرمستقیم با آنها کار کردهاید زیرا روش ()main حاوی رشته اصلی. هر زمان که روش main()
را اجرا کردید، Thread
اصلی را نیز اجرا کرده اید.
مطالعه کلاس Thread
برای درک نحوه عملکرد رشته در برنامه های جاوا بسیار مفید است. همانطور که در اینجا نشان داده شده است، می توانیم با فراخوانی روش currentThread().getName()
به رشته ای که در حال اجرا است دسترسی داشته باشیم:
public class MainThread {
public static void main(String... mainThread) {
System.out.println(Thread.currentThread().getName());
}
}
این کد “اصلی” را چاپ میکند و موضوعی را که در حال حاضر اجرا میشود، شناسایی میکند. دانستن نحوه شناسایی رشته در حال اجرا اولین گام برای جذب مفاهیم رشته است.
چرخه حیات رشته جاوا
هنگام کار با نخ ها، مهم است که از وضعیت نخ آگاه باشید. چرخه حیات رشته جاوا از شش حالت رشته تشکیل شده است:
- جدید: یک
Thread()
جدید ایجاد شده است. - قابل اجرا: روش
start()
Thread
فراخوانی شده است. - در حال اجرا: روش
start()
فراخوانی شده است و رشته در حال اجرا است. - Suspended: رشته به طور موقت به حالت تعلیق درآمده است و می تواند توسط رشته دیگری از سر گرفته شود.
- مسدود شده: موضوع منتظر فرصتی برای اجرا است. این زمانی اتفاق میافتد که یک رشته قبلاً متد
synchronized()
را فراخوانی کرده باشد و رشته بعدی باید تا پایان آن صبر کند. - خاتمه یافت: اجرای رشته کامل شد.
شکل ۱. شش حالت چرخه حیات رشته های جاوا
چیزهای بیشتری برای کاوش و درک در مورد وضعیت های رشته وجود دارد، اما اطلاعات موجود در شکل ۱ در حال حاضر کافی است.
توسعه کلاس موضوع
در ساده ترین حالت، پردازش همزمان با گسترش یک کلاس Thread
انجام می شود، همانطور که در اینجا نشان داده شده است:
public class InheritingThread extends Thread {
InheritingThread(String threadName) {
super(threadName);
}
public static void main(String... inheriting) {
System.out.println(Thread.currentThread().getName() + " is running");
new InheritingThread("inheritingThread").start();
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " is running");
}
}
در اینجا، ما دو رشته را اجرا می کنیم: MainThread
و InheritingThread
. وقتی روش start()
را با inheritingThread()
جدید فراخوانی می کنیم، منطق در روش run()
اجرا می شود.
ما همچنین نام رشته دوم را در سازنده کلاس Thread
ارسال می کنیم، بنابراین خروجی این خواهد بود:
main is running.
inheritingThread is running.
رابط Runnable
بهجای استفاده از وراثت، میتوانید هدف قابل اجرا. عبور Runnable
در سازنده Thread
منجر به اتصال کمتر و انعطاف پذیری بیشتر می شود. پس از عبور از Runnable
، میتوانیم متد start()
را دقیقاً مانند مثال قبلی فراخوانی کنیم:
public class RunnableThread implements Runnable {
public static void main(String... runnableThread) {
System.out.println(Thread.currentThread().getName());
new Thread(new RunnableThread()).start();
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
رشته های غیر شبح در مقابل دیمون
از نظر اجرا، دو نوع رشته وجود دارد:
- رشته های غیر دیمون تا انتها اجرا می شوند. thread اصلی نمونه خوبی از یک نخ غیر دیمون است. کد موجود در
main()
همیشه تا پایان اجرا خواهد شد، مگر اینکهSystem.exit()
برنامه را مجبور به تکمیل کند. - یک رشته شبح برعکس است، اساساً فرآیندی است که تا پایان لازم نیست اجرا شود.
قاعده را به خاطر بسپارید: اگر یک رشته غیر شبح محصور قبل از یک رشته شبح پایان یابد، رشته دیمون تا پایان اجرا نخواهد شد.
برای درک بهتر رابطه thread های daemon و non-daemon، این مثال را مطالعه کنید:
import java.util.stream.IntStream;
public class NonDaemonAndDaemonThread {
public static void main(String... nonDaemonAndDaemon) throws InterruptedException {
System.out.println("Starting the execution in the Thread " + Thread.currentThread().getName());
Thread daemonThread = new Thread(() -> IntStream.rangeClosed(1, 100000)
.forEach(System.out::println));
daemonThread.setDaemon(true);
daemonThread.start();
Thread.sleep(10);
System.out.println("End of the execution in the Thread " +
Thread.currentThread().getName());
}
}
در این مثال من از یک رشته شبح برای اعلام محدوده ای از ۱ تا ۱۰۰۰۰۰، تکرار همه آنها و سپس چاپ استفاده کرده ام. اما به یاد داشته باشید، اگر رشته اصلی غیر دیمون ابتدا به پایان برسد، یک نخ شبح اجرا نمی شود.
خروجی به صورت زیر انجام می شود:
- شروع اجرا در رشته اصلی.
- اعداد از ۱ تا احتمالاً ۱۰۰۰۰۰ را چاپ کنید.
- پایان اجرا در رشته اصلی، به احتمال زیاد قبل از تکرار تا ۱۰۰۰۰۰ کامل می شود.
خروجی نهایی به اجرای JVM شما بستگی دارد.
همانطور که می بینید، رشته ها غیرقابل پیش بینی هستند.
کد منبع را دریافت کنید
کد را برای این چالش جاوا دریافت کنید. شما می توانید تست های خود را در حالی که از مثال ها پیروی می کنید، اجرا کنید.
اولویت موضوع و JVM
این امکان وجود دارد که اجرای رشته را با روش setPriority
اولویت بندی کنید، اما، دوباره، نحوه مدیریت آن به پیاده سازی JVM بستگی دارد. لینوکس، macOS و Windows همگی پیادهسازیهای JVM متفاوتی دارند و هر کدام اولویت رشته را طبق پیشفرضها مدیریت میکنند.
اولویت رشته ای که تنظیم می کنید بر ترتیب فراخوانی رشته تأثیر می گذارد. سه ثابت اعلام شده در کلاس Thread
عبارتند از:
/**
* The minimum priority that a thread can have.
*/
public static final int MIN_PRIORITY = 1;
/**
* The default priority that is assigned to a thread.
*/
public static final int NORM_PRIORITY = 5;
/**
* The maximum priority that a thread can have.
*/
public static final int MAX_PRIORITY = 10;
آزمایشها را روی کد زیر امتحان کنید تا ببینید در نهایت با چه اولویتی اجرا میشوید:
public class ThreadPriority {
public static void main(String... threadPriority) {
Thread moeThread = new Thread(() -> System.out.println("Moe"));
Thread barneyThread = new Thread(() -> System.out.println("Barney"));
Thread homerThread = new Thread(() -> System.out.println("Homer"));
moeThread.setPriority(Thread.MAX_PRIORITY);
barneyThread.setPriority(Thread.NORM_PRIORITY);
homerThread.setPriority(Thread.MIN_PRIORITY);
homerThread.start();
barneyThread.start();
moeThread.start();
}
}
حتی اگر moeThread
را بهعنوان MAX_PRIORITY
تنظیم کنیم، نمیتوانیم روی این موضوع حساب کنیم که ابتدا این رشته اجرا میشود. در عوض، ترتیب اجرا تصادفی خواهد بود.
یادداشتی در مورد ثابت ها در مقابل تعداد عدد
کلاس Thread
با اولین نسخه جاوا معرفی شد. در آن زمان، اولویت ها با استفاده از ثابت ها تنظیم می شد، نه تعداد. با این حال، در استفاده از ثابت ها مشکلی وجود دارد: اگر عدد اولویتی را که در محدوده ۱ تا ۱۰ قرار ندارد ارسال کنیم، روش setPriority()
یک IllegalArgumentException. امروز، میتوانیم از enums برای حل این مشکل استفاده کنیم. استفاده از enum ها انتقال یک آرگومان غیرقانونی را غیرممکن می کند، که هم کد را ساده می کند و هم کنترل بیشتری بر اجرای آن به ما می دهد.
چه چیزی را در مورد موضوعات جاوا به خاطر بسپارید
- روش
start()
را برای شروع یکThread
فراخوانی کنید. - امکان گسترش مستقیم کلاس
Thread
برای استفاده از رشته ها وجود دارد. - این امکان وجود دارد که یک عمل رشته را در یک رابط
Runnable
پیاده سازی کنید. - اولویت موضوع به پیاده سازی JVM بستگی دارد.
- رفتار نخ به پیاده سازی JVM نیز بستگی دارد.
- اگر یک رشته غیر شبح محصور ابتدا به پایان برسد، یک رشته شبح کامل نمیشود.
اشتباهات رایج در موضوعات جاوا
- فراخوانی روش
run()
راهی برای شروع یک رشته جدید نیست. - دوبار تلاش برای شروع یک رشته باعث ایجاد
IllegalThreadStateException
می شود. - از اجازه دادن به چندین فرآیند برای تغییر وضعیت یک شی اجتناب کنید.
- منطق برنامهای را که به اولویت رشته متکی است ننویسید (نمیتوانید آن را پیشبینی کنید).
- به ترتیب اجرای thread متکی نباشید – حتی اگر یک نخ را ابتدا شروع کنید، هیچ تضمینی وجود ندارد که ابتدا آن را اجرا کنید.
چالش موضوعات جاوا را قبول کنید!
شما فقط چند چیز در مورد رشته های جاوا یاد گرفته اید، بنابراین بیایید یک چالش جاوا را امتحان کنیم تا آموخته های خود را آزمایش کنیم.
public class ThreadChallenge {
private static int wolverineAdrenaline = 10;
public static void main(String... doYourBest) {
new Motorcycle("Harley Davidson").start();
Motorcycle fastBike = new Motorcycle("Dodge Tomahawk");
fastBike.setPriority(Thread.MAX_PRIORITY);
fastBike.setDaemon(false);
fastBike.start();
Motorcycle yamaha = new Motorcycle("Yamaha YZF");
yamaha.setPriority(Thread.MIN_PRIORITY);
yamaha.start();
}
static class Motorcycle extends Thread {
Motorcycle(String bikeName) { super(bikeName); }
@Override public void run() {
wolverineAdrenaline++;
if (wolverineAdrenaline == 13) {
System.out.println(this.getName());
}
}
}
}
به نظر شما خروجی این کد چه خواهد بود؟ در اینجا گزینه ها وجود دارد:
A. هارلی دیویدسون
بی. دوج تاماهاوک
C. یاماها YZF
D. نامشخص
حل چالش
در کد بالا، سه رشته ایجاد کردیم. اولین رشته Harley Davidson
است و ما به این رشته اولویت پیشفرض را اختصاص دادیم. رشته دوم Dodge Tomahawk
است که به MAX_PRIORITY
اختصاص داده شده است. سومین YZF یاماها
، با MIN_PRIORITY
است. سپس موضوعات را شروع کردیم.
برای تعیین ترتیب اجرای رشتهها، ابتدا باید توجه داشته باشید که کلاس Motorcycle
کلاس Thread
را گسترش میدهد و ما نام رشته را در آن ارسال کردهایم. سازنده ما همچنین روش run()
را با یک شرط لغو کردهایم: if (wolverineAdrenaline == 13)
.
با وجود اینکه YZF یاماها
سومین رشته در ترتیب اجرای ما است و دارای MIN_PRIORITY
است، هیچ تضمینی وجود ندارد که برای همه پیادهسازیهای JVM آخرین رشته اجرا شود.< /p>
همچنین ممکن است توجه داشته باشید که در این مثال ما رشته Dodge Tomahawk
را به عنوان daemon
تنظیم کردهایم. از آنجا که این یک رشته شبح است، Dodge Tomahawk
ممکن است هرگز اجرا را کامل نکند. اما دو رشته دیگر به طور پیشفرض غیر دیمون هستند، بنابراین رشتههای Harley Davidson
و YZF یاماها
قطعا اجرای خود را کامل خواهند کرد.
برای نتیجه گیری، نتیجه D: نامشخص خواهد بود. این به این دلیل است که هیچ تضمینی وجود ندارد که زمانبندی رشته از ترتیب اجرا یا اولویت رشته ما پیروی کند.
به یاد داشته باشید، نمیتوانیم برای پیشبینی ترتیب اجرای JVM به منطق برنامه (ترتیب رشتهها یا اولویت رشتهها) تکیه کنیم.
چالش ویدیویی! اشکال زدایی آرگومان های متغیر
اشکالزدایی یکی از سادهترین راهها برای جذب کامل مفاهیم برنامهنویسی و در عین حال بهبود کد شماست. در این ویدیو میتوانید در حین اشکالزدایی و توضیح چالش رفتار رشته، همراه باشید:
درباره جاوا بیشتر بدانید
- نکات سریع کد را دریافت کنید: همه مقالات رافائل را در سری InfoWorld Java Challengers بخوانید.
- همه ویدیوهای موجود در لیست پخش ویدیوی جاوا Challengers.
- در وبلاگ چالشگران جاوا رافائل و در کتاب او، جاوا چلنجرهای بیشتری را با بیش از ۷۰ چالش کد.
پست های مرتبط
رفتار رشته در JVM
رفتار رشته در JVM
رفتار رشته در JVM