معرفی
بنابراین، ما می دانیم که موضوعاتی در جاوا وجود دارد که می توانید در بررسی " You Can't Spoil Java with a Thread: Part I - Threads " در مورد آنها بخوانید. برای انجام کار به طور همزمان به نخ ها نیاز است. بنابراین، به احتمال زیاد نخ ها به نحوی با یکدیگر تعامل خواهند داشت. بیایید بفهمیم که چگونه این اتفاق می افتد و چه کنترل های اساسی داریم.بازده
متد Thread.yield() مرموز است و به ندرت استفاده می شود. تغییرات زیادی در توضیحات آن در اینترنت وجود دارد. تا جایی که برخی در مورد نوعی صف نخ ها می نویسند که در آن موضوع با در نظر گرفتن اولویت های آنها به سمت پایین حرکت می کند. شخصی می نویسد که موضوع وضعیت خود را از حالت در حال اجرا به قابل اجرا تغییر می دهد (اگرچه هیچ تقسیمی به این وضعیت ها وجود ندارد و جاوا بین آنها تمایز قائل نمی شود). اما در واقعیت، همه چیز بسیار ناشناخته تر و به یک معنا ساده تر است. در مبحث مستندسازی روش،yield
باگ " JDK-6416721: (رشته مشخصات) Fix Thread.yield() javadoc " وجود دارد. اگر آن را بخوانید، واضح است که در واقع این روش yield
فقط توصیه هایی را به زمانبندی رشته جاوا می دهد که می توان زمان اجرای کمتری به این رشته داد. اما اینکه واقعاً چه اتفاقی میافتد، اینکه آیا زمانبند توصیه را میشنود و به طور کلی چه کاری انجام میدهد به اجرای JVM و سیستم عامل بستگی دارد. یا شاید از برخی عوامل دیگر. همه سردرگمی ها به احتمال زیاد به دلیل تجدید نظر در مورد چند رشته ای در طول توسعه زبان جاوا بود. می توانید در بررسی " معرفی مختصر به جاوا Thread.yield() " بیشتر بخوانید.
خواب - به خواب رفتن نخ
یک نخ ممکن است در حین اجرای آن به خواب رود. این ساده ترین نوع تعامل با موضوعات دیگر است. سیستم عاملی که ماشین مجازی جاوا بر روی آن نصب شده است، جایی که کد جاوا در آن اجرا می شود، زمانبندی رشته خود را دارد که Thread Scheduler نام دارد. این اوست که تصمیم می گیرد کدام رشته را در چه زمانی اجرا کند. برنامهنویس نمیتواند مستقیماً از طریق کد جاوا با این زمانبند تعامل داشته باشد، اما میتواند از طریق JVM از زمانبندیکننده بخواهد تا موضوع را برای مدتی مکث کند و آن را به حالت Sleep قرار دهد. می توانید در مقاله های " Thread.sleep() " و " چگونه Multithreading کار می کند " بیشتر بخوانید. علاوه بر این، می توانید نحوه کار رشته ها در سیستم عامل ویندوز را بیابید: " Internals of Windows Thread ". حالا با چشمان خود خواهیم دید. بیایید کد زیر را در یک فایل ذخیره کنیمHelloWorldApp.java
:
class HelloWorldApp {
public static void main(String []args) {
Runnable task = () -> {
try {
int secToWait = 1000 * 60;
Thread.currentThread().sleep(secToWait);
System.out.println("Waked up");
} catch (InterruptedException e) {
e.printStackTrace();
}
};
Thread thread = new Thread(task);
thread.start();
}
}
همانطور که می بینید، ما یک وظیفه داریم که 60 ثانیه صبر می کند و پس از آن برنامه به پایان می رسد. ما کامپایل javac HelloWorldApp.java
و اجرا می کنیم java HelloWorldApp
. بهتر است در یک پنجره جداگانه راه اندازی شود. به عنوان مثال، در ویندوز به این صورت خواهد بود: start java HelloWorldApp
. با استفاده از دستور jps، PID فرآیند را می یابیم و لیست رشته ها را با استفاده از jvisualvm --openpid pidПроцесса
: همانطور که می بینید، موضوع ما وارد وضعیت Sleeping شده است. در واقع، خواباندن موضوع فعلی را می توان به زیبایی بیشتری انجام داد:
try {
TimeUnit.SECONDS.sleep(60);
System.out.println("Waked up");
} catch (InterruptedException e) {
e.printStackTrace();
}
احتمالاً متوجه شده اید که ما همه جا پردازش می کنیم InterruptedException
؟ بیایید بفهمیم چرا.
قطع کردن یک موضوع یا Thread.interrupt
مسئله این است که در حالی که نخ در خواب منتظر است، ممکن است کسی بخواهد این انتظار را قطع کند. در این مورد، ما چنین استثنایی را مدیریت می کنیم.Thread.stop
این کار پس از اعلام منسوخ شدن روش انجام شد ، یعنی. قدیمی و نامطلوب برای استفاده دلیل این امر این بود که وقتی روش فراخوانی شد، stop
موضوع به سادگی "کشته شد" که بسیار غیرقابل پیش بینی بود. ما نمیتوانستیم بدانیم چه زمانی جریان متوقف میشود، نمیتوانیم ثبات دادهها را تضمین کنیم. تصور کنید که داده ها را روی یک فایل می نویسید و سپس جریان از بین می رود. بنابراین، ما تصمیم گرفتیم که منطقی تر است که جریان را نکشیم، بلکه به آن اطلاع دهیم که باید قطع شود. نحوه واکنش به این موضوع به خود جریان بستگی دارد. جزئیات بیشتر را میتوانید در Oracle پیدا کنید " Whythread.stop منسوخ شده است؟ " بیایید به یک مثال نگاه کنیم:
public static void main(String []args) {
Runnable task = () -> {
try {
TimeUnit.SECONDS.sleep(60);
} catch (InterruptedException e) {
System.out.println("Interrupted");
}
};
Thread thread = new Thread(task);
thread.start();
thread.interrupt();
}
در این مثال، ما 60 ثانیه منتظر نخواهیم بود، بلکه بلافاصله «وقفه شده» را چاپ خواهیم کرد. این به این دلیل است که ما متد thread را فراخوانی کردیم interrupt
. این روش "پرچم داخلی به نام وضعیت وقفه" را تنظیم می کند. یعنی هر رشته دارای یک پرچم داخلی است که مستقیماً قابل دسترسی نیست. اما ما روش های بومی برای تعامل با این پرچم داریم. اما این تنها راه نیست. یک نخ می تواند در حال اجرا باشد، نه منتظر چیزی، بلکه صرفاً اقداماتی را انجام دهد. اما می تواند فراهم کند که آنها بخواهند آن را در نقطه خاصی از کار خود تکمیل کنند. مثلا:
public static void main(String []args) {
Runnable task = () -> {
while(!Thread.currentThread().isInterrupted()) {
//Do some work
}
System.out.println("Finished");
};
Thread thread = new Thread(task);
thread.start();
thread.interrupt();
}
در مثال بالا، می بینید که حلقه while
تا زمانی که thread به صورت خارجی قطع شود اجرا می شود. نکته مهمی که باید در مورد پرچم isInterrupted بدانید این است که اگر آن را بگیریم InterruptedException
، پرچم isInterrupted
تنظیم مجدد می شود و سپس isInterrupted
false برمی گردد. همچنین یک متد ثابت برای کلاس Thread وجود دارد که فقط برای رشته فعلی اعمال می شود - Thread.interrupted() ، اما این متد پرچم را به false بازنشانی می کند! می توانید در بخش " قطع موضوع " بیشتر بخوانید.
بپیوندید - در انتظار تکمیل تاپیک دیگر
ساده ترین نوع انتظار، انتظار برای تکمیل یک رشته دیگر است.public static void main(String []args) throws InterruptedException {
Runnable task = () -> {
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
System.out.println("Interrupted");
}
};
Thread thread = new Thread(task);
thread.start();
thread.join();
System.out.println("Finished");
}
در این مثال، موضوع جدید به مدت 5 ثانیه میخوابد. در عین حال نخ اصلی منتظر می ماند تا نخ خواب بیدار شود و کار خود را تمام کند. اگر از طریق JVisualVM نگاه کنید، وضعیت thread به این صورت خواهد بود: به لطف ابزارهای نظارتی، می توانید ببینید که چه اتفاقی در thread می افتد. این روش join
بسیار ساده است، زیرا به سادگی یک روش با کد جاوا است که wait
در حالی اجرا می شود که رشته ای که روی آن فراخوانی می شود زنده است. هنگامی که نخ از بین می رود (در پایان)، انتظار خاتمه می یابد. این تمام جادوی روش است join
. بنابراین، بیایید به جالب ترین بخش برویم.
مانیتور مفهومی
در multithreading چیزی به نام Monitor وجود دارد. به طور کلی، کلمه مانیتور از لاتین به عنوان "نظارت" یا "نظارت" ترجمه شده است. در چارچوب این مقاله، ما سعی خواهیم کرد ماهیت را به خاطر بسپاریم، و برای کسانی که می خواهند، از شما می خواهم برای جزئیات بیشتر به مطالب از پیوندها بپردازید. بیایید سفر خود را با مشخصات زبان جاوا شروع کنیم، یعنی با JLS: " 17.1. Synchronization ". در زیر آمده است: معلوم می شود که برای همگام سازی بین موضوعات، جاوا از مکانیزم خاصی به نام "مانیتور" استفاده می کند. هر شی دارای یک نمایشگر مرتبط با آن است و رشته ها می توانند آن را قفل یا باز کنند. در مرحله بعد، ما یک آموزش آموزشی را در وب سایت Oracle خواهیم دید: " قفل های درونی و همگام سازی ". این آموزش توضیح می دهد که همگام سازی در جاوا حول یک موجودیت داخلی به نام قفل ذاتی یا قفل مانیتور ساخته شده است. اغلب چنین قفلی به سادگی "مانیتور" نامیده می شود. همچنین می بینیم که هر شی در جاوا دارای یک قفل ذاتی مرتبط با آن است. می توانید " جاوا - قفل های درونی و همگام سازی " را بخوانید. در مرحله بعد، مهم است که بفهمیم چگونه یک شی در جاوا می تواند با یک مانیتور مرتبط شود. هر شی در جاوا دارای یک هدر است - نوعی ابرداده داخلی که از طریق کد در دسترس برنامه نویس نیست، اما ماشین مجازی برای کار صحیح با اشیاء به آن نیاز دارد. هدر شی شامل یک MarkWord است که به شکل زیر است:https://edu.netbeans.org/contrib/slides/java-overview-and-java-se6.pdf
public class HelloWorld{
public static void main(String []args){
Object object = new Object();
synchronized(object) {
System.out.println("Hello World");
}
}
}
بنابراین، با استفاده از کلمه کلیدی، synchronized
رشته فعلی (که در آن این خطوط کد اجرا می شود) سعی می کند از مانیتور مرتبط با شی استفاده کند object
و "قفل بگیرید" یا "نمایشگر را بگیرید" (گزینه دوم حتی ترجیح داده می شود). اگر در مورد مانیتور بحثی وجود نداشته باشد (یعنی هیچ کس دیگری نمی خواهد روی یک شی همگام شود)، جاوا می تواند سعی کند بهینه سازی به نام "قفل بایاس" را انجام دهد. عنوان شیء در Mark Word حاوی برچسب مربوطه و رکوردی است که مانیتور به کدام رشته متصل است. این امر باعث کاهش هزینه های اضافی در هنگام گرفتن مانیتور می شود. اگر مانیتور قبلاً به نخ دیگری بسته شده باشد، این قفل کافی نیست. JVM به نوع قفل بعدی - قفل اولیه تغییر می کند. از عملیات مقایسه و تعویض (CAS) استفاده می کند. در همان زمان، هدر در Mark Word دیگر خود Mark Word را ذخیره نمی کند، اما یک پیوند به ذخیره سازی آن + برچسب تغییر می کند تا JVM متوجه شود که ما از قفل اولیه استفاده می کنیم. اگر برای مانیتور چندین رشته اختلاف وجود داشته باشد (یکی مانیتور را گرفته است و دومی منتظر رها شدن مانیتور باشد)، تگ در Mark Word تغییر می کند و Mark Word شروع به ذخیره یک مرجع به مانیتور به عنوان یک شی - یک موجودیت داخلی JVM. همانطور که در JEP گفته شد، در این مورد، فضایی در ناحیه حافظه Native Heap برای ذخیره این موجودیت مورد نیاز است. پیوند به محل ذخیره سازی این موجودیت داخلی در شی Mark Word قرار خواهد گرفت. بنابراین، همانطور که می بینیم، مانیتور واقعا مکانیزمی برای اطمینان از همگام سازی دسترسی رشته های متعدد به منابع مشترک است. چندین پیاده سازی از این مکانیسم وجود دارد که JVM بین آنها سوئیچ می کند. بنابراین، برای سادگی، وقتی در مورد مانیتور صحبت می کنیم، در واقع در مورد قفل صحبت می کنیم.
همگام سازی شده و در انتظار قفل است
مفهوم مانیتور، همانطور که قبلاً دیدیم، ارتباط نزدیکی با مفهوم "بلوک همگام سازی" (یا همانطور که به آن بخش بحرانی نیز گفته می شود) دارد. بیایید به یک مثال نگاه کنیم:public static void main(String[] args) throws InterruptedException {
Object lock = new Object();
Runnable task = () -> {
synchronized (lock) {
System.out.println("thread");
}
};
Thread th1 = new Thread(task);
th1.start();
synchronized (lock) {
for (int i = 0; i < 8; i++) {
Thread.currentThread().sleep(1000);
System.out.print(" " + i);
}
System.out.println(" ...");
}
}
در اینجا، نخ اصلی ابتدا وظیفه را به یک موضوع جدید ارسال می کند و سپس بلافاصله قفل را "گرفته" و یک عملیات طولانی را با آن انجام می دهد (8 ثانیه). در تمام این مدت، وظیفه نمی تواند وارد بلوک برای اجرای آن شود synchronized
، زیرا قفل قبلاً اشغال شده است. اگر نخی نتواند قفل را بدست آورد، در مانیتور منتظر آن می ماند. به محض دریافت آن، به اجرا ادامه خواهد داد. هنگامی که یک نخ از مانیتور خارج می شود، قفل را آزاد می کند. در JVisualVM به این صورت است: همانطور که می بینید، وضعیت در JVisualVM "مانیتور" نامیده می شود زیرا موضوع مسدود شده است و نمی تواند مانیتور را اشغال کند. همچنین می توانید وضعیت thread را در کد پیدا کنید، اما نام این حالت با اصطلاحات JVisualVM مطابقت ندارد، اگرچه آنها مشابه هستند. در این مورد، th1.getState()
حلقه BLOCKEDfor
برمی گردد ، زیرا در حالی که حلقه در حال اجرا است، مانیتور توسط نخ اشغال می شود و نخ مسدود می شود و نمی تواند تا زمانی که قفل بازگردد به کار خود ادامه دهد. علاوه بر بلوک های همگام سازی، کل روش را می توان همگام سازی کرد. به عنوان مثال، متدی از کلاس : lock
main
th1
HashTable
public synchronized int size() {
return count;
}
در یک واحد زمان، این روش تنها توسط یک نخ اجرا خواهد شد. اما ما به یک قفل نیاز داریم، درست است؟ بله من به آن نیاز دارم. در مورد روش های شی، قفل خواهد بود this
. بحث جالبی در مورد این موضوع وجود دارد: " آیا استفاده از روش همگام به جای بلوک همگام مزیتی دارد؟ ". اگر متد ثابت باشد، قفل نخواهد بود this
(زیرا برای یک متد استاتیک نمی تواند باشد this
)، بلکه شی کلاس خواهد بود (به عنوان مثال، Integer.class
).
منتظر و منتظر روی مانیتور باشید. متدهای notify و notifyAll
Thread روش انتظار دیگری دارد که به مانیتور متصل می شود. بر خلافsleep
و join
، نمی توان آن را فقط نامید. و نام او است wait
. این متد wait
روی شیئی اجرا می شود که می خواهیم در مانیتور آن منتظر بمانیم. بیایید یک مثال را ببینیم:
public static void main(String []args) throws InterruptedException {
Object lock = new Object();
// task будет ждать, пока его не оповестят через lock
Runnable task = () -> {
synchronized(lock) {
try {
lock.wait();
} catch(InterruptedException e) {
System.out.println("interrupted");
}
}
// После оповещения нас мы будем ждать, пока сможем взять лок
System.out.println("thread");
};
Thread taskThread = new Thread(task);
taskThread.start();
// Ждём и после этого забираем себе лок, оповещаем и отдаём лок
Thread.currentThread().sleep(3000);
System.out.println("main");
synchronized(lock) {
lock.notify();
}
}
در JVisualVM به این صورت خواهد بود: برای درک اینکه چگونه این کار می کند، باید به یاد داشته باشید که روش ها wait
به . عجیب به نظر می رسد که روش های مرتبط با موضوع در . اما پاسخ در اینجا نهفته است. همانطور که به یاد داریم، هر شی در جاوا یک هدر دارد. هدر حاوی اطلاعات خدمات مختلفی است، از جمله اطلاعات مربوط به مانیتور - اطلاعات مربوط به وضعیت قفل. و همانطور که به یاد داریم، هر شی (یعنی هر نمونه) با یک موجودیت داخلی JVM به نام قفل ذاتی، که مانیتور نیز نامیده می شود، ارتباط دارد. در مثال بالا، وظیفه توضیح میدهد که بلوک همگامسازی را در مانیتور مرتبط با وارد میکنیم . اگر امکان گرفتن قفل روی این مانیتور وجود دارد، پس . رشته ای که این وظیفه را اجرا می کند، مانیتور را آزاد می کند ، اما به صف رشته هایی که منتظر اعلان در مانیتور هستند می پیوندد . این صف از رشته ها WAIT-SET نامیده می شود که به طور صحیح تری ماهیت را منعکس می کند. این بیشتر یک مجموعه است تا یک صف. موضوع یک موضوع جدید با وظیفه وظیفه ایجاد می کند، آن را شروع می کند و 3 ثانیه صبر می کند. این به احتمال زیاد به یک نخ جدید اجازه می دهد تا قفل را قبل از نخ گرفته و روی مانیتور در صف قرار بگیرد. پس از آن نخ خود وارد بلوک همگام سازی می شود و اعلان موضوع را روی مانیتور انجام می دهد. پس از ارسال اعلان، رشته مانیتور را رها می کند و رشته جدید (که قبلاً منتظر بود) پس از انتظار برای رها شدن مانیتور به اجرای خود ادامه می دهد. ارسال یک اعلان به یکباره فقط به یکی از رشته ها ( ) یا به همه رشته های موجود در صف امکان پذیر است ( ). میتوانید در « تفاوت بین notify() و notifyAll() در جاوا » بیشتر بخوانید. توجه به این نکته مهم است که ترتیب اطلاع رسانی به پیاده سازی JVM بستگی دارد. میتوانید در « چگونه گرسنگی را با notify و notifyall حل کنیم؟ » بیشتر بخوانید . همگام سازی را می توان بدون تعیین یک شی انجام داد. این می تواند زمانی انجام شود که نه یک بخش جداگانه از کد، بلکه یک روش کامل همگام سازی شده باشد. به عنوان مثال، برای متدهای استاتیک، قفل شی کلاس خواهد بود (که از طریق ): notify
java.lang.Object
Object
lock
wait
lock
lock
main
main
main
lock
main
lock
lock
notify
notifyAll
.class
public static synchronized void printA() {
System.out.println("A");
}
public static void printB() {
synchronized(HelloWorld.class) {
System.out.println("B");
}
}
از نظر استفاده از قفل هر دو روش یکسان است. اگر روش ثابت نباشد، همگام سازی مطابق جریان انجام می شود instance
، یعنی مطابق با this
. ضمناً قبلاً گفتیم که با استفاده از روش getState
می توانید وضعیت یک موضوع را بدست آورید. wait
بنابراین در اینجا یک رشته است که توسط مانیتور در صف قرار می گیرد، اگر روش محدودیت زمانی برای انتظار تعیین کرده باشد، وضعیت WAITING یا TIMED_WAITING خواهد بود .
چرخه حیات یک نخ
همانطور که دیدیم، جریان در جریان زندگی وضعیت خود را تغییر می دهد. در اصل، این تغییرات چرخه زندگی نخ هستند. هنگامی که یک موضوع به تازگی ایجاد می شود، دارای وضعیت NEW است. در این موقعیت، هنوز شروع نشده است و جاوا Thread Scheduler هنوز چیزی در مورد موضوع جدید نمی داند. برای اینکه زمانبندی رشته در مورد یک موضوع بداند، باید با شماره تماس بگیریدthread.start()
. سپس موضوع به حالت RUNNABLE می رود. بسیاری از طرح های نادرست در اینترنت وجود دارد که در آن حالت های Runnable و Running از هم جدا می شوند. اما این یک اشتباه است، زیرا ... جاوا بین وضعیت های "آماده برای اجرا" و "در حال اجرا" تفاوتی قائل نمی شود. وقتی رشته ای زنده است اما فعال نیست (قابل اجرا نیست)، در یکی از دو حالت است:
- مسدود شده - منتظر ورود به بخش محافظت شده است، یعنی. به
synchonized
بلوک - WAITING - بر اساس یک شرط منتظر رشته دیگری است. اگر شرط درست باشد، زمانبندی رشته موضوع را شروع میکند.
getState
. Thread ها نیز متدی دارند isAlive
که در صورت پایان نشدن رشته، مقدار true را برمی گرداند.
LockSupport و پارکینگ نخ
از جاوا 1.6 مکانیزم جالبی به نام LockSupport وجود داشت . این کلاس یک "مجوز" یا مجوز را با هر رشته ای که از آن استفاده می کند مرتبط می کند. فراخوانی متدpark
در صورت وجود مجوز فوراً برمی گردد و همان مجوز را در طول تماس اشغال می کند. در غیر این صورت مسدود است. فراخوانی روش unpark
باعث می شود مجوز در صورتی که قبلاً در دسترس نباشد در دسترس باشد. تنها 1 مجوز وجود دارد. در Java API، LockSupport
یک Semaphore
. بیایید به یک مثال ساده نگاه کنیم:
import java.util.concurrent.Semaphore;
public class HelloWorldApp{
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(0);
try {
semaphore.acquire();
} catch (InterruptedException e) {
// Просим разрешение и ждём, пока не получим его
e.printStackTrace();
}
System.out.println("Hello, World!");
}
}
این کد برای همیشه منتظر خواهد ماند زیرا سمافور اکنون مجوز 0 دارد. و هنگامی که در کد فراخوانی می شود acquire
(یعنی درخواست مجوز)، رشته منتظر می ماند تا مجوز را دریافت کند. از آنجایی که منتظر هستیم، موظف به پردازش آن هستیم InterruptedException
. جالب اینجاست که یک سمافور حالت نخ جداگانه ای را پیاده سازی می کند. اگر به JVisualVM نگاه کنیم، می بینیم که حالت ما Wait نیست، بلکه پارک است. بیایید به مثال دیگری نگاه کنیم:
public static void main(String[] args) throws InterruptedException {
Runnable task = () -> {
//Запаркуем текущий поток
System.err.println("Will be Parked");
LockSupport.park();
// Как только нас распаркуют - начнём действовать
System.err.println("Unparked");
};
Thread th = new Thread(task);
th.start();
Thread.currentThread().sleep(2000);
System.err.println("Thread state: " + th.getState());
LockSupport.unpark(th);
Thread.currentThread().sleep(2000);
}
وضعیت رشته WAITING خواهد بود، اما JVisualVM wait
بین از synchronized
و park
از تمایز قائل می شود LockSupport
. چرا این یکی اینقدر مهم است LockSupport
؟ بیایید دوباره به API جاوا برگردیم و وضعیت موضوع انتظار را بررسی کنیم . همانطور که می بینید، تنها سه راه برای ورود به آن وجود دارد. 2 راه - این wait
و join
. و سومی این است LockSupport
. قفل ها در جاوا بر اساس همان اصول ساخته شده اند LockSupport
و ابزارهای سطح بالاتر را نشان می دهند. بیایید سعی کنیم از یکی استفاده کنیم. بیایید به عنوان مثال نگاه کنیم ReentrantLock
:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class HelloWorld{
public static void main(String []args) throws InterruptedException {
Lock lock = new ReentrantLock();
Runnable task = () -> {
lock.lock();
System.out.println("Thread");
lock.unlock();
};
lock.lock();
Thread th = new Thread(task);
th.start();
System.out.println("main");
Thread.currentThread().sleep(2000);
lock.unlock();
}
}
مانند نمونه های قبلی، همه چیز در اینجا ساده است. lock
منتظر است تا کسی منبعی را منتشر کند. اگر به JVisualVM نگاه کنیم، می بینیم که رشته جدید تا زمانی که main
thread به آن قفل ندهد، پارک می شود. در اینجا می توانید اطلاعات بیشتری در مورد قفل ها بخوانید: " برنامه نویسی چند رشته ای در جاوا 8. قسمت دوم. همگام سازی دسترسی به اشیاء قابل تغییر " و " Java Lock API. نظریه و مثال استفاده ." برای درک بهتر پیاده سازی قفل ها، خواندن در مورد Phazer در نمای کلی " کلاس Phaser " مفید است. و در مورد همگامکنندههای مختلف صحبت میکنید، باید مقاله Habré " Java.util.concurrent.* Synchronizers Reference " را بخوانید.
GO TO FULL VERSION