CodeGym /בלוג Java /Random-HE /ההבדל בין מוטקס, מוניטור וסמפור
John Squirrels
רָמָה
San Francisco

ההבדל בין מוטקס, מוניטור וסמפור

פורסם בקבוצה
היי! כאשר למדת ריבוי שרשורים ב-CodeGym, נתקלת לעתים קרובות במושגים של "מוטקס" ו"מוניטור". בלי להציץ, אתה יכול להגיד במה הם שונים? :) אם כן, כל הכבוד! אם לא (זה הכי נפוץ), זה לא מפתיע. "Mutex" ו"מוניטור" הם למעשה מושגים קשורים. בנוסף, כשאתה קורא שיעורים ותצפה בסרטונים על ריבוי שרשורים באתרים אחרים, אתה תיתקל במושג דומה נוסף: "סמפורה". יש לו גם פונקציה דומה מאוד למוניטורים ולמוטקסים. זו הסיבה שאנחנו הולכים לחקור את שלושת המונחים האלה. נסתכל על כמה דוגמאות ונגיע להבנה מוחלטת של איך מושגים אלה שונים זה מזה :)

מנעול

מוטקס (או מנעול) הוא מנגנון מיוחד לסנכרון חוטים. אחד "מצורף" לכל אובייקט ב-Java - אתה כבר יודע את זה :) זה לא משנה אם אתה משתמש במחלקות סטנדרטיות או יוצר מחלקות משלך, למשל Cat and Dog : לכל האובייקטים של כל המחלקות יש mutex . המונח "מוטקס" בא מ"הדרה הדדית", שמתאר בצורה מושלמת את מטרתו. כפי שאמרנו באחד השיעורים הקודמים שלנו, mutex מאפשר להבטיח שלשרשור אחד בכל פעם יש גישה לאובייקט. דוגמה פופולרית בחיים האמיתיים של mutex כוללת שירותים. כאשר אדם נכנס למחיצת שירותים, הוא נועל את הדלת מבפנים. האסלה היא כמו חפץ שניתן לגשת אליו באמצעות חוטים מרובים. המנעול על דלת המחיצה הוא כמו מוטקס, וקו האנשים בחוץ מייצג חוטים. המנעול בדלת הוא המוטקס של האסלה: הוא מבטיח שרק אדם אחד יכול להיכנס פנימה. מה ההבדל בין mutex, מוניטור וסמפור?  - 2במילים אחרות, רק שרשור אחד בכל פעם יכול לעבוד עם משאבים משותפים. ניסיונות של שרשורים אחרים (אנשים) לקבל גישה למשאבים תפוסים ייכשלו. למוטקס יש כמה תכונות חשובות. ראשית , רק שני מצבים אפשריים: "לא נעול" ו"נעול". זה עוזר לנו להבין איך זה עובד: אתה יכול לצייר הקבלות עם משתנים בוליאניים (נכון/לא נכון) או מספרים בינאריים (0/1). שנית , לא ניתן לשלוט ישירות במדינה. לג'אווה אין מנגנון שיאפשר לך לקחת אובייקט במפורש, לקבל את ה-mutex שלו ולהקצות את הסטטוס הרצוי. במילים אחרות, אתה לא יכול לעשות משהו כמו:

Object myObject = new Object();
Mutex mutex = myObject.getMutex();
mutex.free();
משמעות הדבר היא שאינך יכול לשחרר את המוטקס של אובייקט. רק למכשיר Java יש גישה ישירה אליו. מתכנתים עובדים עם מוטקסים באמצעות הכלים של השפה.

צג

מוניטור הוא "מבנה על" נוסף על מוטקס. למעשה, מוניטור הוא גוש קוד ש"בלתי נראה" למתכנת. כשדיברנו על מוטקסים קודם לכן, נתנו דוגמה פשוטה:

public class Main {

   private Object obj = new Object();

   public void doSomething() {

       // ...some logic, available for all threads

       synchronized (obj) {

           // Logic available to just one thread at a time
       }
   }
}
בבלוק הקוד המסומן במילת המפתח המסונכרנת , נרכש המוטקס של אובייקט האובייקט שלנו. מצוין, אנחנו יכולים לרכוש את המנעול, אבל איך בדיוק מסופקת ה"הגנה"? כאשר אנו רואים את המילה מסונכרנת , מה מונע מהשרשורים האחרים להיכנס לבלוק? ההגנה מגיעה ממוניטור! המהדר ממיר את מילת המפתח המסונכרנת לכמה פיסות קוד מיוחדות. שוב, בואו נחזור לדוגמה שלנו עם שיטת doSomething() . נוסיף לזה:

public class Main {

   private Object obj = new Object();

   public void doSomething() {

       // ...some logic, available for all threads

       // Logic available to just one thread at a time
       synchronized (obj) {

           /* Do important work that requires that the object
           be accessed by only one thread */
           obj.someImportantMethod();
       }
   }
}
הנה מה שקורה "מתחת למכסה המנוע" לאחר שהמהדר ממיר את הקוד הזה:

public class Main {

   private Object obj = new Object();

   public void doSomething() throws InterruptedException {

       // ...some logic, available for all threads

       // Logic available to just one thread at a time:
     
       /* as long as the object's mutex is busy,
       all the other threads (except the one that acquired it) are put to sleep */
       while (obj.getMutex().isBusy()) {
           Thread.sleep(1);
       }

       // Mark the object's mutex as busy
       obj.getMutex().isBusy() = true;

       /* Do important work that requires that the object
       be accessed by only one thread */
       obj.someImportantMethod();

       // Free the object's mutex
       obj.getMutex().isBusy() = false;
   }
}
כמובן, זו לא דוגמה אמיתית. כאן השתמשנו בקוד דמוי ג'אווה כדי לתאר מה קורה בתוך מכונת ה-Java. עם זאת, פסאודו-קוד זה נותן הבנה מצוינת של מה שקורה בפועל עם האובייקט והחוטים בתוך הבלוק המסונכרן וכיצד המהדר ממיר את מילת המפתח הזו למספר הצהרות ש"בלתי נראים" למתכנת. בעיקרון, Java משתמשת במילת המפתח המסונכרנת כדי לייצג צג . כל הקוד שמופיע במקום מילת המפתח המסונכרנת בדוגמה האחרונה הוא המוניטור.

סֵמָפוֹר

מילה נוספת שתפגשו במחקר האישי שלכם על ריבוי שרשורים היא "סמפורה". בואו להבין מה זה ומה זה שונה ממוניטור ומוטקס. סמפור הוא כלי לסנכרון גישה למשאב כלשהו. המאפיין הייחודי שלו הוא שהוא משתמש במונה כדי ליצור את מנגנון הסנכרון. המונה אומר לנו כמה שרשורים יכולים לגשת בו זמנית למשאב המשותף. מה ההבדל בין mutex, מוניטור וסמפור?  - 3סמפורים בג'אווה מיוצגים על ידי מחלקת סמפור . בעת יצירת אובייקטי סמפור, אנו יכולים להשתמש בבנאים הבאים:

Semaphore(int permits)
Semaphore(int permits, boolean fair)
אנו מעבירים את הדברים הבאים לבנאי:
    int permits - הערך ההתחלתי והמקסימלי של המונה. במילים אחרות, פרמטר זה קובע כמה שרשורים יכולים לגשת בו-זמנית למשאב המשותף;
  • boolean fair - קובע את הסדר שבו האשכולות יקבלו גישה. אם הוגן הוא נכון, אזי ניתנת גישה לשרשורים ממתינים בסדר שבו הם ביקשו זאת. אם הוא שקר, הסדר נקבע על ידי מתזמן השרשור.
דוגמה קלאסית לשימוש בסמפור היא בעיית פילוסוף האוכל. מה ההבדל בין mutex, מוניטור וסמפור?  - 4כדי להקל על ההבנה, נפשט אותה מעט. תארו לעצמכם שיש לנו 5 פילוסופים שצריכים לאכול ארוחת צהריים. בנוסף, יש לנו שולחן אחד שיכול להכיל בו זמנית לא יותר משני אנשים. המשימה שלנו היא להאכיל את כל הפילוסופים. אף אחד מהם לא צריך להיות רעב, ואף אחד מהם לא צריך "לחסום" אחד את השני כאשר מנסים להתיישב ליד השולחן (עלינו למנוע מבוי סתום). כך ייראה כיתת הפילוסוף שלנו:

class Philosopher extends Thread {

   private Semaphore sem;

   // Did the philosopher eat?
   private boolean full = false;

   private String name;

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

   public void run()
   {
       try
       {
           // If the philosopher has not eaten
           if (!full) {
               // Ask the semaphore for permission to run
               sem.acquire();
               System.out.println(name + " takes a seat at the table");

               // The philosopher eats
               sleep(300);
               full = true;

               System.out.println(name + " has eaten! He leaves the table");
               sem.release();

               // The philosopher leaves, making room for others
               sleep(300);
           }
       }
       catch(InterruptedException e) {
           System.out.println("Something went wrong!");
       }
   }
}
והנה הקוד להפעלת התוכנית שלנו:

public class Main {

   public static void main(String[] args) {

       Semaphore sem = new Semaphore(2);
       new Philosopher(sem, "Socrates").start();
       new Philosopher(sem,"Plato").start();
       new Philosopher(sem,"Aristotle").start();
       new Philosopher(sem, "Thales").start();
       new Philosopher(sem, "Pythagoras").start();
   }
}
יצרנו סמפור שהמונה שלו מוגדר ל-2 כדי לספק את התנאי: רק שני פילוסופים יכולים לאכול בו זמנית. כלומר, רק שני שרשורים יכולים לרוץ בו-זמנית, כי כיתת הפילוסוף שלנו יורשת את Thread ! שיטות ה-acquisition() ו- release() של מחלקת Semaphore שולטות במונה הגישה שלה. שיטת ה-acquisition() מבקשת מהסמפור גישה למשאב. אם המונה הוא >0, אזי ניתנת גישה והמונה מצטמצם ב-1. שיטת release() "משחררת" את הגישה שניתנה קודם לכן, ומחזירה אותה למונה (מגדילה את מונה הגישה של הסמפור ב-1). מה אנחנו מקבלים כשאנחנו מריצים את התוכנית? האם הבעיה נפתרה? האם הפילוסופים שלנו לא ילחמו בזמן שהם ממתינים לתורם? :) הנה פלט הקונסולה שקיבלנו:

Socrates takes a seat at the table 
Plato takes a seat at the table 
Socrates has eaten! He leaves the table
Plato has eaten! He leaves the table 
Aristotle takes a seat at the table 
Pythagoras takes a seat at the table 
Aristotle has eaten! He leaves the table
Pythagoras has eaten! He leaves the table 
Thales takes a seat at the table 
Thales has eaten! He leaves the table
עשינו זאת! ולמרות שתאלס נאלץ לסעוד לבד, אני לא חושב שפגענו בו :) אולי שמתם לב לכמה קווי דמיון בין מוטקס לסמפור. אכן, יש להם אותה משימה: לסנכרן גישה למשאב כלשהו. מה ההבדל בין mutex, מוניטור וסמפור?  - 5ההבדל היחיד הוא שניתן לרכוש את המוטקס של אובייקט על ידי חוט אחד בלבד בכל פעם, בעוד שבמקרה של סמפור, המשתמש במונה חוטים, מספר חוטים יכולים לגשת למשאב בו-זמנית. זה לא רק צירוף מקרים :) מוטקס הוא למעשה סמפור עם ספירה של 1. במילים אחרות, זה סמפור שיכול להכיל חוט בודד. זה ידוע גם בתור "סמפור בינארי" מכיוון שהמונה שלו יכול לכלול רק 2 ערכים - 1 ("לא נעול") ו-0 ("נעול"). זהו זה! כפי שאתה יכול לראות, זה לא כל כך מבלבל אחרי הכל :) עכשיו, אם אתה רוצה ללמוד multithreading בפירוט רב יותר באינטרנט, יהיה לך קצת יותר קל לנווט במושגים האלה. נתראה בשיעורים הבאים!
הערות
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION