سلام! هنگام مطالعه چند رشته ای در JavaRush، اغلب با مفاهیم "mutex" و "monitor" مواجه شدید. اکنون می توانید بدون نگاه کردن به تفاوت آنها پاسخ دهید؟ :) تفاوت بین موتکس، مانیتور و سمافور چیست - 1اگه تونستی، آفرین! اگر نه (و اغلب این اتفاق می افتد) - جای تعجب نیست. مفاهیم "mutex" و "مانیتور" در واقع به هم مرتبط هستند. علاوه بر این، هنگام خواندن سخنرانی ها و تماشای فیلم های چند رشته ای در منابع خارجی در اینترنت، با مفهوم مشابه دیگری روبرو خواهید شد - "سمافور". عملکرد آن نیز تا حد زیادی شبیه به مانیتور و موتکس است. بنابراین، بیایید این سه اصطلاح را درک کنیم، به چند نمونه نگاه کنیم و در نهایت درک تفاوت آنها با یکدیگر را در ذهن خود سازماندهی کنیم :)

موتکس

mutex یک شی خاص برای همگام سازی نخ ها است. به هر شی در جاوا "ضمیمه" شده است - شما قبلاً می دانید :) مهم نیست که از کلاس های استاندارد استفاده می کنید یا کلاس های خود را ایجاد می کنید، مثلاً Catو Dog: همه اشیاء همه کلاس ها دارای mutex هستند . نام "mutex" از انگلیسی "Mutual EXclusion" - "mutual Exclusion" گرفته شده است و این کاملاً هدف آن را نشان می دهد. همانطور که در یکی از سخنرانی های قبلی گفتیم، وظیفه mutex این است که چنین مکانیزمی را فراهم کند تا فقط یک رشته در یک زمان خاص به یک شی دسترسی داشته باشد . یک تشبیه رایج برای موتکس در زندگی واقعی، «مثال توالت» است. وقتی فردی وارد توالت می شود در را از داخل قفل می کند. توالت به عنوان یک شی عمل می کند که می توان با نخ های متعدد به آن دسترسی پیدا کرد. قفل در توالت نقش موتکس است و صف افراد بیرون نقش نخ است. قفل روی در یک mutex توالت است: این اطمینان را می دهد که فقط یک نفر می تواند در یک زمان داخل باشد. تفاوت بین موتکس، مانیتور و سمافور چیست - 2به عبارت دیگر، تنها یک رشته در یک زمان می تواند روی منابع مشترک کار کند. تلاش سایر رشته ها (افراد) برای دسترسی به منابع اشغال شده با شکست مواجه خواهد شد. موتکس چندین ویژگی مهم دارد. اولا ، فقط دو حالت ممکن است - "رایگان" و "مشغول". این کار درک نحوه کارکرد آن را آسان‌تر می‌کند: می‌توان موازی‌ها را با متغیرهای بولی true/false یا سیستم اعداد باینری 1/0 رسم کرد. ثانیاً ، دولت ها را نمی توان مستقیماً کنترل کرد. هیچ مکانیزمی در جاوا وجود ندارد که به شما اجازه دهد صریحاً یک شی را بگیرید، mutex آن را دریافت کنید و وضعیت مورد نظر را به آن اختصاص دهید. به عبارت دیگر، شما نمی توانید کاری مانند:
Object myObject = new Object();
Mutex mutex = myObject.getMutex();
mutex.free();
بنابراین، mutex جسم نمی تواند آزاد شود. فقط ماشین جاوا دسترسی مستقیم به آن دارد. برنامه نویسان با استفاده از ابزارهای زبانی با mutexe ها کار می کنند.

نظارت کنید

مانیتور یک "افزونه" اضافی برای mutex است. در واقع، مانیتور یک قطعه کد است که برای برنامه نویس نامرئی است . قبلاً در مورد mutex صحبت کردیم، یک مثال ساده آوردیم:
public class Main {

   private Object obj = new Object();

   public void doSomething() {

       //...some logic available to all threads

       synchronized (obj) {

           //logic that is only available to one thread at a time
       }
   }
}
در بلوک کدی که با کلمه مشخص شده است synchronized، mutex شی ما گرفته می شود obj. خوب، دستگیری اتفاق می‌افتد، اما «مکانیسم دفاعی» دقیقاً چگونه به دست می‌آید؟ چرا رشته های دیگر وقتی کلمه ای را می بینند synchronizedنمی توانند به داخل بلوک بروند ؟ این مانیتور است که مکانیسم محافظ را ایجاد می کند! کامپایلر کلمه را synchronizedبه چند قطعه کد خاص تبدیل می کند. یک بار دیگر اجازه دهید به مثال خود با متد برگردیم doSomething()و به آن اضافه کنیم:
public class Main {

   private Object obj = new Object();

   public void doSomething() {

       //...some logic available to all threads

       //logic that is only available to one thread at a time
       synchronized (obj) {

           /*выполнить важную работу, при которой доступ к an objectу
           должен быть только у одного потока*/
           obj.someImportantMethod();
       }
   }
}
بعد از اینکه کامپایلر این کد را تبدیل کرد، چه اتفاقی می افتد "زیر سرپوش" برنامه ما:
public class Main {

   private Object obj = new Object();

   public void doSomething() throws InterruptedException {

       //...some logic available to all threads

       //логика, которая одновременно доступна только для одного потока:

       /*до тех пор, пока мьютекс an object занят -
       любой другой поток (кроме того, который его захватил), спит*/
       while (obj.getMutex().isBusy()) {
           Thread.sleep(1);
       }

       //пометить мьютекс an object How занятый
       obj.getMutex().isBusy() = true;

       /*выполнить важную работу, при которой доступ к an objectу
       должен быть только у одного потока*/
       obj.someImportantMethod();

       //освободить мьютекс an object
       obj.getMutex().isBusy() = false;
   }
}
مثال البته واقعی نیست. در اینجا، با استفاده از کدهای جاوا مانند، سعی کردیم آنچه را که در این لحظه در داخل ماشین جاوا اتفاق می افتد منعکس کنیم. با این حال، این شبه کد درک خوبی از آنچه در واقع با شی و رشته‌های داخل بلاک اتفاق می‌افتد synchronizedو اینکه چگونه کامپایلر این کلمه را به چندین دستور تبدیل می‌کند که برای برنامه‌نویس نامرئی هستند، می‌دهد. اساسا مانیتور در جاوا با استفاده از کلمه بیان می شودsynchronized . تمام کدهایی که به جای کلمه synchronizedدر مثال آخر ظاهر شد، مانیتور است.

سمافور

کلمه دیگری که هنگام مطالعه چند رشته ای به تنهایی با آن مواجه می شوید «سمافور» است. بیایید بفهمیم که چیست و چه تفاوتی با مانیتور و موتکس دارد. سمافور وسیله ای برای همگام سازی دسترسی به یک منبع است. ویژگی آن این است که هنگام ایجاد مکانیسم هماهنگ سازی از یک شمارنده استفاده می کند. شمارنده به ما می گوید که چند رشته به طور همزمان می توانند به یک منبع مشترک دسترسی داشته باشند. تفاوت بین موتکس، مانیتور و سمافور چیست - 3سمافورها در جاوا با کلاس نمایش داده می شوند Semaphore. هنگام ایجاد اشیاء سمافور، می توانیم از سازنده های زیر استفاده کنیم:
Semaphore(int permits)
Semaphore(int permits, boolean fair)
ما به سازنده منتقل می کنیم:
  • int permits- مقدار اولیه و حداکثر شمارنده. یعنی چند رشته می توانند به طور همزمان به یک منبع مشترک دسترسی داشته باشند.

  • boolean fair- برای ایجاد ترتیبی که در آن موضوعات دسترسی دریافت می کنند. اگر fair= true ، به رشته های منتظر به ترتیبی که آنها آن را درخواست کرده اند، دسترسی داده می شود. اگر نادرست باشد ، ترتیب توسط زمانبندی رشته تعیین می شود.

یک مثال کلاسیک از استفاده از سمافورها مسئله فیلسوفان ناهار است .
تفاوت بین موتکس، مانیتور و سمافور چیست - 4
برای درک بهتر شرایط آن را کمی ساده می کنیم. تصور کنید ما 5 فیلسوف داریم که به ناهار نیاز دارند. در عین حال، ما یک میز داریم و بیش از دو نفر نمی توانند همزمان در آن باشند. وظیفه ما این است که همه فیلسوفان را تغذیه کنیم. هیچ‌کدام از آنها نباید گرسنه بمانند، و همچنین نباید هنگام تلاش برای نشستن پشت میز یکدیگر را «مسدود» کنند (ما باید از بن‌بست جلوگیری کنیم). این چیزی است که کلاس فیلسوف ما به نظر می رسد:
class Philosopher extends Thread {

   private Semaphore sem;

   // поел ли философ
   private boolean full = false;

   private String name;

   Philosopher(Semaphore sem, String name) {
       this.sem=sem;
       this.name=name;
   }

   public void run()
   {
       try
       {
           // если философ еще не ел
           if (!full) {
               //Запрашиваем у семафора разрешение на выполнение
               sem.acquire();
               System.out.println (name + " садится за стол");

               // философ ест
               sleep(300);
               full = true;

               System.out.println (name + " поел! Он выходит из-за стола");
               sem.release();

               // философ ушел, освободив место другим
               sleep(300);
           }
       }
       catch(InterruptedException e) {
           System.out.println ("What-то пошло не так!");
       }
   }
}
و کد اجرای برنامه ما اینجاست:
public class Main {

   public static void main(String[] args) {

       Semaphore sem = new Semaphore(2);
       new Philosopher(sem,"Сократ").start();
       new Philosopher(sem,"Платон").start();
       new Philosopher(sem,"Аристотель").start();
       new Philosopher(sem,"Фалес").start();
       new Philosopher(sem,"Пифагор").start();
   }
}
ما یک سمافور با شمارش 2 ایجاد کردیم تا این شرط را برآورده کنیم که فقط دو فیلسوف بتوانند همزمان غذا بخورند. یعنی فقط دو رشته می توانند به طور همزمان کار کنند، زیرا کلاس ما Philosopherاز وارث شده است Thread! کلاس acquire()و متدها شمارنده مجوز آن را کنترل می کنند. این روش برای دسترسی به یک منبع از سمافور مجوز درخواست می کند. اگر شمارنده > 0 باشد، مجوز داده می شود و شمارنده با 1 کاهش می یابد. وقتی برنامه را اجرا می کنیم چه چیزی بدست می آوریم؟ آیا مشکل حل شده است آیا فیلسوفان ما در انتظار نوبت خود می جنگند؟ :) این خروجی کنسولی است که دریافت کردیم: سقراط سر میزی می نشیند افلاطون سر میزی می نشیند که سقراط خورده است! میز را ترک می کند افلاطون خورده است! سفره را ترک می کند ارسطو سر سفره می نشیند فیثاغورث سر سفره ای می نشیند که ارسطو خورده است! سفره ای را که فیثاغورث خورده است ترک می کند! سفره را ترک می کند تالس سر سفره ای می نشیند که تالس خورده است! او میز را ترک می کند ما موفق شدیم! و اگرچه تالس مجبور بود به تنهایی شام بخورد، من فکر می‌کنم او از دست ما عصبانی نیست :) ممکن است متوجه شباهت‌هایی بین موتکس و سمافور شده باشید. به طور کلی، آنها هدف یکسانی دارند: همگام سازی دسترسی به برخی منابع. تنها تفاوت این است که mutex یک شی را می توان تنها توسط یک رشته در یک زمان بدست آورد، در حالی که در مورد سمافور، از یک شمارنده رشته استفاده می شود و چندین نفر از آنها می توانند به طور همزمان به منبع دسترسی داشته باشند. و این فقط یک شباهت تصادفی نیست :) در واقع، mutex یک سمافور تک مکان است . یعنی سمافوری است که شمارنده‌اش در ابتدا روی 1 تنظیم شده است. به آن «سمافور باینری» نیز گفته می‌شود زیرا شمارنده آن تنها می‌تواند 2 مقدار داشته باشد - 1 ("رایگان") و 0 ("مشغول"). همین! همانطور که می بینید، همه چیز چندان گیج کننده نبود :) حالا اگر می خواهید موضوع چند رشته ای را با جزئیات بیشتری در اینترنت مطالعه کنید، پیمایش مفاهیم برای شما کمی آسان تر خواهد بود. در درس های بعدی می بینمت! release()Semaphoreacquire()release()تفاوت بین موتکس، مانیتور و سمافور چیست - 5